SeedShield 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {seedshield-0.2.1/SeedShield.egg-info → seedshield-0.2.2}/PKG-INFO +1 -1
- {seedshield-0.2.1 → seedshield-0.2.2/SeedShield.egg-info}/PKG-INFO +1 -1
- {seedshield-0.2.1 → seedshield-0.2.2}/pyproject.toml +1 -1
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/config.py +1 -1
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/display_handler.py +17 -8
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/input_handler.py +58 -29
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/secure_word_interface.py +14 -7
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/state_handler.py +3 -1
- {seedshield-0.2.1 → seedshield-0.2.2}/setup.py +1 -1
- {seedshield-0.2.1 → seedshield-0.2.2}/LICENSE +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/MANIFEST.in +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/README.md +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/SeedShield.egg-info/SOURCES.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/SeedShield.egg-info/dependency_links.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/SeedShield.egg-info/entry_points.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/SeedShield.egg-info/requires.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/SeedShield.egg-info/top_level.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/pre_build.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/__init__.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/data/english.txt +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/main.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/secure_memory.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/seedshield/ui_manager.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/setup.cfg +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/__init__.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/conftest.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_display_handler.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_fixtures.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_input_handler.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_main.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_secure_word_interface.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_state_handler.py +0 -0
- {seedshield-0.2.1 → seedshield-0.2.2}/tests/test_ui_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: SeedShield
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Secure BIP39 word viewer with masking and reveal functionality
|
|
5
5
|
Author-email: Barlog951 <barlog951@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://github.com/Barlog951/SeedShield/blob/main/README.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: SeedShield
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Secure BIP39 word viewer with masking and reveal functionality
|
|
5
5
|
Author-email: Barlog951 <barlog951@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://github.com/Barlog951/SeedShield/blob/main/README.md
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "SeedShield"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
9
9
|
description = "Secure BIP39 word viewer with masking and reveal functionality"
|
|
10
10
|
authors = [
|
|
@@ -10,6 +10,7 @@ from typing import List, Optional, Any
|
|
|
10
10
|
|
|
11
11
|
from .config import logger, MASK_CHARACTER, MASK_LENGTH
|
|
12
12
|
from .config import SCROLL_INDICATOR_UP, SCROLL_INDICATOR_DOWN, MENU_TEXT
|
|
13
|
+
from .state_handler import StateHandler
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class DisplayHandler:
|
|
@@ -35,6 +36,7 @@ class DisplayHandler:
|
|
|
35
36
|
self.ui_manager = ui_manager
|
|
36
37
|
self.mask = MASK_CHARACTER * MASK_LENGTH
|
|
37
38
|
self.legacy_mask = self.mask # For backward compatibility with tests
|
|
39
|
+
self.state_handler: Optional[StateHandler] = None # Will be set later
|
|
38
40
|
|
|
39
41
|
# Store the last state to optimize rendering
|
|
40
42
|
self._last_height = 0
|
|
@@ -42,8 +44,10 @@ class DisplayHandler:
|
|
|
42
44
|
|
|
43
45
|
logger.debug("DisplayHandler initialized with %s words", len(words))
|
|
44
46
|
|
|
45
|
-
def _add_scroll_indicators(
|
|
46
|
-
|
|
47
|
+
def _add_scroll_indicators(
|
|
48
|
+
self, stdscr: Any, visible_start: int, visible_end: int,
|
|
49
|
+
positions: List[int], height: int, width: int
|
|
50
|
+
) -> None:
|
|
47
51
|
"""
|
|
48
52
|
Add scroll indicators to the display if needed.
|
|
49
53
|
|
|
@@ -94,6 +98,7 @@ class DisplayHandler:
|
|
|
94
98
|
|
|
95
99
|
# Show reset option only if we've reached the last word
|
|
96
100
|
menu_text = MENU_TEXT["with_reset"] if is_last_reached else MENU_TEXT["standard"]
|
|
101
|
+
# We no longer show the wordlist info on any screen to avoid blocking words
|
|
97
102
|
|
|
98
103
|
# Split menu text across lines if it's too long
|
|
99
104
|
if len(menu_text) > self._last_width - 2:
|
|
@@ -168,8 +173,10 @@ class DisplayHandler:
|
|
|
168
173
|
logger.warning("Invalid word position: %s", pos)
|
|
169
174
|
return f"INVALID({pos})"
|
|
170
175
|
|
|
171
|
-
def display_words(
|
|
172
|
-
|
|
176
|
+
def display_words(
|
|
177
|
+
self, stdscr: Any, positions: List[int], scroll_position: int,
|
|
178
|
+
cursor_pos: Optional[int], is_last_reached: bool
|
|
179
|
+
) -> int:
|
|
173
180
|
"""
|
|
174
181
|
Display words with masking in the terminal interface.
|
|
175
182
|
|
|
@@ -213,10 +220,12 @@ class DisplayHandler:
|
|
|
213
220
|
result: int = visible_end - visible_start
|
|
214
221
|
return result
|
|
215
222
|
|
|
216
|
-
def _display_visible_words(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
def _display_visible_words(
|
|
224
|
+
self, stdscr: Any, positions: List[int],
|
|
225
|
+
visible_start: int, visible_end: int,
|
|
226
|
+
scroll_position: int, cursor_pos: Optional[int],
|
|
227
|
+
height: int, width: int
|
|
228
|
+
) -> None:
|
|
220
229
|
"""
|
|
221
230
|
Display the visible words on the screen.
|
|
222
231
|
|
|
@@ -187,6 +187,50 @@ class InputHandler:
|
|
|
187
187
|
logger.error("Unexpected error reading positions file: %s", str(e))
|
|
188
188
|
return None
|
|
189
189
|
|
|
190
|
+
def _display_error(self, stdscr: Any, message: str, error: Optional[Exception] = None) -> None:
|
|
191
|
+
"""
|
|
192
|
+
Display an error message on the screen.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
stdscr: Curses window object
|
|
196
|
+
message: Error message to display
|
|
197
|
+
error: Optional exception to log
|
|
198
|
+
"""
|
|
199
|
+
if error:
|
|
200
|
+
logger.error("%s: %s", message, str(error))
|
|
201
|
+
stdscr.addstr(6, 0, message)
|
|
202
|
+
stdscr.refresh()
|
|
203
|
+
time.sleep(1)
|
|
204
|
+
|
|
205
|
+
def _process_input_command(self, stdscr: Any, input_str: str) -> Optional[List[int]]:
|
|
206
|
+
"""
|
|
207
|
+
Process a specific input command.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
stdscr: Curses window object
|
|
211
|
+
input_str: User input string
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Optional[List[int]]: Processed positions or None
|
|
215
|
+
"""
|
|
216
|
+
# Handle quitting
|
|
217
|
+
if input_str == 'q':
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
# Handle clipboard input
|
|
221
|
+
if input_str == 'v':
|
|
222
|
+
return self.process_clipboard_input(stdscr)
|
|
223
|
+
|
|
224
|
+
# Handle individual number
|
|
225
|
+
validated_input = self.validate_number_input(input_str)
|
|
226
|
+
if validated_input:
|
|
227
|
+
return validated_input
|
|
228
|
+
|
|
229
|
+
# Invalid input
|
|
230
|
+
err_msg = f"Invalid input. Please enter a number between 1-{self.word_count}"
|
|
231
|
+
self._display_error(stdscr, err_msg)
|
|
232
|
+
return [] # Empty list indicates continuing input loop
|
|
233
|
+
|
|
190
234
|
def get_input(self, stdscr: Any) -> Optional[List[int]]:
|
|
191
235
|
"""
|
|
192
236
|
Get user input, validating it and handling different input types.
|
|
@@ -198,45 +242,30 @@ class InputHandler:
|
|
|
198
242
|
Optional[List[int]]: List of valid position numbers or None for quit command
|
|
199
243
|
"""
|
|
200
244
|
while True:
|
|
245
|
+
# Clear any previous error messages
|
|
246
|
+
stdscr.move(6, 0)
|
|
247
|
+
stdscr.clrtoeol()
|
|
201
248
|
self.display_input_prompt(stdscr)
|
|
202
249
|
curses.echo()
|
|
203
250
|
|
|
204
251
|
try:
|
|
205
252
|
input_str = stdscr.getstr().decode('utf-8').strip().lower()
|
|
253
|
+
# Skip empty input
|
|
254
|
+
if not input_str:
|
|
255
|
+
continue
|
|
206
256
|
|
|
207
|
-
|
|
208
|
-
|
|
257
|
+
result = self._process_input_command(stdscr, input_str)
|
|
258
|
+
# None means quit, empty list means continue, otherwise return the result
|
|
259
|
+
if result is None:
|
|
209
260
|
return None
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if input_str == 'v':
|
|
213
|
-
clipboard_numbers = self.process_clipboard_input(stdscr)
|
|
214
|
-
if clipboard_numbers:
|
|
215
|
-
return clipboard_numbers
|
|
216
|
-
|
|
217
|
-
# Handle individual number
|
|
218
|
-
validated_input = self.validate_number_input(input_str)
|
|
219
|
-
if validated_input:
|
|
220
|
-
return validated_input
|
|
221
|
-
|
|
222
|
-
stdscr.addstr(6, 0, f"Invalid input. Please enter a number between 1-{self.word_count}")
|
|
223
|
-
stdscr.refresh()
|
|
224
|
-
time.sleep(1)
|
|
261
|
+
if result: # Non-empty list
|
|
262
|
+
return result
|
|
225
263
|
|
|
226
264
|
except UnicodeDecodeError as e:
|
|
227
|
-
|
|
228
|
-
stdscr.addstr(6, 0, "Invalid character input")
|
|
229
|
-
stdscr.refresh()
|
|
230
|
-
time.sleep(1)
|
|
265
|
+
self._display_error(stdscr, "Invalid character input", e)
|
|
231
266
|
except ValueError as e:
|
|
232
|
-
|
|
233
|
-
stdscr.addstr(6, 0, "Invalid input format")
|
|
234
|
-
stdscr.refresh()
|
|
235
|
-
time.sleep(1)
|
|
267
|
+
self._display_error(stdscr, "Invalid input format", e)
|
|
236
268
|
except Exception as e:
|
|
237
|
-
|
|
238
|
-
stdscr.addstr(6, 0, "Error processing input")
|
|
239
|
-
stdscr.refresh()
|
|
240
|
-
time.sleep(1)
|
|
269
|
+
self._display_error(stdscr, "Error processing input", e)
|
|
241
270
|
finally:
|
|
242
271
|
curses.noecho()
|
|
@@ -45,6 +45,8 @@ class SecureWordInterface:
|
|
|
45
45
|
self.input_handler = InputHandler(len(self.words), self.ui_manager)
|
|
46
46
|
self.display_handler = DisplayHandler(self.words, self.ui_manager)
|
|
47
47
|
self.state_handler = StateHandler()
|
|
48
|
+
# Set state_handler reference in display_handler
|
|
49
|
+
self.display_handler.state_handler = self.state_handler
|
|
48
50
|
|
|
49
51
|
# For backward compatibility with tests
|
|
50
52
|
self._initialize_curses = self.ui_manager.initialize
|
|
@@ -179,8 +181,10 @@ class SecureWordInterface:
|
|
|
179
181
|
|
|
180
182
|
return should_reinit, new_positions, new_scroll
|
|
181
183
|
|
|
182
|
-
def _process_user_input(
|
|
183
|
-
|
|
184
|
+
def _process_user_input(
|
|
185
|
+
self, stdscr: Any, positions: List[int], scroll_position: int,
|
|
186
|
+
visible_count: int, current_time: float
|
|
187
|
+
) -> Tuple[bool, int]:
|
|
184
188
|
"""
|
|
185
189
|
Process user input and update state accordingly.
|
|
186
190
|
|
|
@@ -206,6 +210,8 @@ class SecureWordInterface:
|
|
|
206
210
|
# Handle timeout adjustment for input mode
|
|
207
211
|
if should_reinit:
|
|
208
212
|
stdscr.timeout(10000)
|
|
213
|
+
# Clear positions to force entering input mode on next loop
|
|
214
|
+
positions.clear()
|
|
209
215
|
|
|
210
216
|
# Handle quit command
|
|
211
217
|
if should_quit:
|
|
@@ -309,9 +315,10 @@ class SecureWordInterface:
|
|
|
309
315
|
except Exception as e:
|
|
310
316
|
logger.debug("Error handling mouse event: %s", str(e))
|
|
311
317
|
|
|
312
|
-
def _handle_user_input(
|
|
313
|
-
|
|
314
|
-
|
|
318
|
+
def _handle_user_input(
|
|
319
|
+
self, c: int, positions: List[int], scroll_position: int,
|
|
320
|
+
visible_count: int, current_time: float
|
|
321
|
+
) -> Tuple[bool, bool, int, List[int]]:
|
|
315
322
|
"""
|
|
316
323
|
Process a single user input and determine actions.
|
|
317
324
|
|
|
@@ -353,11 +360,11 @@ class SecureWordInterface:
|
|
|
353
360
|
def _get_load_positions_func(self) -> Callable[[str], Optional[List[int]]]:
|
|
354
361
|
"""Return a function that loads positions from a file."""
|
|
355
362
|
return self.input_handler.load_positions_from_file
|
|
356
|
-
|
|
363
|
+
|
|
357
364
|
def _get_handle_autoscroll_func(self) -> Callable[[Optional[int], int, int], int]:
|
|
358
365
|
"""Return a function that handles autoscrolling."""
|
|
359
366
|
return self.display_handler.handle_autoscroll
|
|
360
|
-
|
|
367
|
+
|
|
361
368
|
def _load_positions_file(self, file_path: str) -> List[int]:
|
|
362
369
|
"""
|
|
363
370
|
Load word positions from a file.
|
|
@@ -138,7 +138,9 @@ class StateHandler:
|
|
|
138
138
|
if positions:
|
|
139
139
|
self.current_index = 0
|
|
140
140
|
|
|
141
|
-
def handle_commands(
|
|
141
|
+
def handle_commands(
|
|
142
|
+
self, key: int, positions: List[int], current_time: float
|
|
143
|
+
) -> Optional[List[int]]:
|
|
142
144
|
"""
|
|
143
145
|
Process user commands and update state accordingly.
|
|
144
146
|
|
|
@@ -23,7 +23,7 @@ class PreBuild(_build_py):
|
|
|
23
23
|
|
|
24
24
|
setup(
|
|
25
25
|
name="seedshield",
|
|
26
|
-
version="0.2.
|
|
26
|
+
version="0.2.2", # This should match VERSION in seedshield/config.py
|
|
27
27
|
|
|
28
28
|
packages=find_namespace_packages(
|
|
29
29
|
include=['seedshield', 'seedshield.*']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|