Exemple #1
0
    def secret_seed(self) -> bytes:
        self._ensure_shards()

        selected: Dict[str, Shard] = {}
        selected_share_id: Optional[int] = None
        selected_shard_ids: set = set()
        selected_shards: set = set()

        satisfied = False
        filled_groups = set()

        while not satisfied:
            shards = [
                shard for (name, shard) in self.shards.items()
                if (name not in selected) and (
                    selected_share_id is None
                    or shard.share_id == selected_share_id) and (
                        shard.shard_id not in selected_shard_ids) and (
                            shard.group_id not in filled_groups)
            ]

            if selected_share_id is not None:
                (enough_shards, _) = check_satisfaction_criteria(
                    selected_shards.union(shards))
                if not enough_shards:
                    print([shard.to_str() for shard in shards])
                    raise HermitError(
                        "There are not enough shards available to unlock this secret."
                    )

            name = None

            try:
                name = self.interface.choose_shard(shards)
            except:
                # catch end-of-input style exceptions
                pass

            if name is None:
                raise HermitError(
                    "Not enough shards selected to unlock secret.")

            shard = self.shards[name]
            if selected_share_id is None:
                selected_share_id = shard.share_id

            selected_shard_ids.add(shard.shard_id)
            selected[name] = shard.words()
            selected_shards.add(shard)

            (satisfied,
             filled_groups) = check_satisfaction_criteria(selected_shards)

        unlocked_mnemonics = list(selected.values())
        return shamir_share.combine_mnemonics(unlocked_mnemonics)
Exemple #2
0
    def copy_shard(self, original: str, copy: str) -> None:
        self._ensure_shards()
        if original not in self.shards:
            raise HermitError("Shard {} does not exist.".format(original))

        if copy in self.shards:
            err_msg = (
                "Shard {} exists. If you need to replace it, delete it first.".format(copy))
            raise HermitError(err_msg)

        original_shard = self.shards[original]
        copy_shard = Shard(
            copy, original_shard.encrypted_mnemonic, interface=self.interface)
        copy_shard.change_password()
        self.shards[copy] = copy_shard
Exemple #3
0
    def _ensure_shards(self, shards_expected: bool = True) -> None:
        if not self._shards_loaded:
            bdata = None

            # If the shards dont exist at the place where they
            # are expected to by, try to restore them with the externally
            # configured getPersistedShards command.
            if not os.path.exists(self.config.shards_file):
                try:
                    os.system(self.config.commands['getPersistedShards'])
                except TypeError:
                    pass

            # If for some reason the persistence layer failed to  create the
            # the shards file, we assume that we just need to initialize
            # it as an empty bson object.
            if not os.path.exists(self.config.shards_file):
                with open(self.config.shards_file, 'wb') as f:
                    f.write(bson.dumps({}))

            with open(self.config.shards_file, 'rb') as f:
                bdata = bson.loads(f.read())

            for name, shard_bytes in bdata.items():
                self.shards[name] = Shard(
                    name, shamir_share.mnemonic_from_bytes(shard_bytes))

            self._shards_loaded = True

        if len(self.shards) == 0 and shards_expected:
            raise HermitError(
                "No shards found.  Create some by entering 'shards' mode.")
Exemple #4
0
 def wrapper(*args, **kwargs):
     try:
         return f(*args, **kwargs)
     except TypeError as terr:
         raise terr
     except Exception as err:
         print(err)
         raise HermitError("Hmm. Something went wrong.")
Exemple #5
0
    def confirm_password(self) -> bytes:
        password = prompt("new password> ",
                          is_password=True).strip().encode('ascii')
        confirm = prompt("     confirm> ",
                         is_password=True).strip().encode('ascii')

        if password == confirm:
            return password

        raise HermitError("Passwords do not match.")
Exemple #6
0
def bip32_sequence(bip32_path: str) -> Tuple[int, ...]:
    """Turn a BIP32 path into a tuple of deriviation points
    """
    bip32_path_regex = "^m(/[0-9]+'?)+$"

    if not match(bip32_path_regex, bip32_path):
        raise HermitError("Not a valid BIP32 path.")

    return tuple(
        _decode_segment(segment) for segment in bip32_path[2:].split('/')
        if len(segment) != 0)
Exemple #7
0
    def input_shard_words(self, name) -> None:
        self._ensure_shards(shards_expected=False)

        if name in self.shards:
            err_msg = ("Shard exists. If you need to replace it, " +
                       "delete it first.")
            raise HermitError(err_msg)

        shard = Shard(name, None, interface=self.interface)
        shard.input()

        self.shards[name] = shard
Exemple #8
0
    def unlock(self, passphrase: str = "") -> None:
        if self.root_priv is not None:
            return

        mnemonic = Mnemonic(self.language)

        # TODO skip wallet words
        words = self.shards.wallet_words()
        if mnemonic.check(words):
            seed = Mnemonic.to_seed(words, passphrase=passphrase)
            self.root_priv = bip32_master_key(seed)
        else:
            raise HermitError("Wallet words failed checksum.")
Exemple #9
0
    def confirm_password(self) -> bytes:
        password = prompt("new password> ",
                          is_password=True).strip().encode('ascii')
        confirm = prompt("     confirm> ",
                         is_password=True).strip().encode('ascii')

        if password == confirm:
            # Empty string means do not encrypt
            if len(password) == 0:
                return None
            return password

        raise HermitError("Passwords do not match.")
Exemple #10
0
    def import_shard_qr(self, name: str, shard_data: bytes) -> None:
        if name in self.shards:
            err_msg = ("Shard exists. If you need to replace it, "
                       + "delete it first.")
            raise HermitError(err_msg)

        shard_dict = bson.loads(shard_data)
        old_name = list(shard_dict.keys())[0]
        print_formatted_text(
            "Importing shard '{}' from qr code as shard '{}'".format(old_name, name))
        shard = Shard(name, None, interface=self.interface)
        shard.from_bytes(shard_dict[old_name])

        self.shards[name] = shard
Exemple #11
0
    def choose_shard(self, shards) -> Optional[str]:

        if len(shards) == 0:
            raise HermitError("Not enough shards to reconstruct secret.")

        shardnames = [shard.name for shard in shards]
        shardnames.sort()

        shardCompleter = WordCompleter(shardnames, sentence=True)

        while True:
            prompt_string = "Choose shard\n(options: {} or <enter> to quit)\n> "
            prompt_msg = prompt_string.format(", ".join(shardnames))
            shard_name = prompt(prompt_msg,
                                completer=shardCompleter,
                                **self.options).strip()

            if shard_name in shardnames:
                return shard_name

            if shard_name == '':
                return None

            print("Shard not found.")