mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
update
This commit is contained in:
@@ -221,12 +221,6 @@ The SeedPass roadmap outlines a structured development plan divided into distinc
|
||||
- Implement synchronization logic to fetch and update entries from Nostr as needed.
|
||||
|
||||
4. **Security Enhancements**
|
||||
- **Parent Seed Recovery**
|
||||
- **Description:** Develop a secure method for users to recover their parent seed if lost.
|
||||
- **Features:**
|
||||
- **Recovery Phrase:** Allow users to generate and store a recovery phrase or backup file.
|
||||
- **Multi-Factor Authentication (MFA):** Integrate MFA to enhance the security of the recovery process.
|
||||
- **Encrypted Storage:** Ensure that recovery data is encrypted and stored securely.
|
||||
- **"Secret" Mode (Clipboard-Only Password Retrieval)**
|
||||
- **Description:** Introduce a "secret" mode where passwords are copied directly to the clipboard rather than displayed on the screen upon retrieval.
|
||||
- **Features:**
|
||||
|
@@ -65,15 +65,15 @@ The following table provides a quick reference to all available advanced CLI com
|
||||
| Post encrypted index to Nostr | `post` | `-P` | `--post` | `seedpass post` |
|
||||
| Retrieve from Nostr | `get-nostr` | `-GN` | `--get-nostr` | `seedpass get-nostr` |
|
||||
| Display Nostr public key | `show-pubkey` | `-K` | `--show-pubkey` | `seedpass show-pubkey` |
|
||||
| **Set Custom Nostr Relays** | `set-relays` | `-SR` | `--set-relays` | `seedpass set-relays --add "wss://relay1.example.com" --add "wss://relay2.example.com"` |
|
||||
| **Enable "Secret" Mode** | `set-secret` | `-SS` | `--set-secret` | `seedpass set-secret --enable` or `seedpass set-secret --disable` |
|
||||
| **Batch Post Index Items to Nostr** | `batch-post` | `-BP` | `--batch-post` | `seedpass batch-post --start 0 --end 9` or `seedpass batch-post --range 10-19` |
|
||||
| **Show All Passwords** | `show-all` | `-SA` | `--show-all` | `seedpass show-all` |
|
||||
| **Add Notes to an Entry** | `add-notes` | `-AN` | `--add-notes` | `seedpass add-notes --index 3 --notes "This is a secured account"` |
|
||||
| **Add Tags to an Entry** | `add-tags` | `-AT` | `--add-tags` | `seedpass add-tags --index 3 --tags "personal,finance"` |
|
||||
| **Search by Tag or Title** | `search-by` | `-SB` | `--search-by` | `seedpass search-by --tag "work"` or `seedpass search-by --title "GitHub"` |
|
||||
| **Automatically Post Index to Nostr After Edit** | `auto-post` | `-AP` | `--auto-post` | `seedpass auto-post --enable` or `seedpass auto-post --disable` |
|
||||
| **Initial Setup Prompt for Seed Generation/Import** | `setup` | `-ST` | `--setup` | `seedpass setup` |
|
||||
| Set Custom Nostr Relays | `set-relays` | `-SR` | `--set-relays` | `seedpass set-relays --add "wss://relay1.example.com" --add "wss://relay2.example.com"` |
|
||||
| Enable "Secret" Mode | `set-secret` | `-SS` | `--set-secret` | `seedpass set-secret --enable` or `seedpass set-secret --disable` |
|
||||
| Batch Post Index Items to Nostr | `batch-post` | `-BP` | `--batch-post` | `seedpass batch-post --start 0 --end 9` or `seedpass batch-post --range 10-19` |
|
||||
| Show All Passwords | `show-all` | `-SA` | `--show-all` | `seedpass show-all` |
|
||||
| Add Notes to an Entry | `add-notes` | `-AN` | `--add-notes` | `seedpass add-notes --index 3 --notes "This is a secured account"` |
|
||||
| Add Tags to an Entry | `add-tags` | `-AT` | `--add-tags` | `seedpass add-tags --index 3 --tags "personal,finance"` |
|
||||
| Search by Tag or Title | `search-by` | `-SB` | `--search-by` | `seedpass search-by --tag "work"` or `seedpass search-by --title "GitHub"` |
|
||||
| Automatically Post Index to Nostr After Edit | `auto-post` | `-AP` | `--auto-post` | `seedpass auto-post --enable` or `seedpass auto-post --disable` |
|
||||
| Initial Setup Prompt for Seed Generation/Import | `setup` | `-ST` | `--setup` | `seedpass setup` |
|
||||
|
||||
---
|
||||
|
||||
@@ -577,7 +577,7 @@ seedpass auto-post --disable
|
||||
**Long Flag:** `--setup`
|
||||
|
||||
**Description:**
|
||||
Guides users through the initial setup process, allowing them to choose between generating a new seed or importing an existing one. This command also handles the encryption of the seed and the creation of a Nostr profile.
|
||||
Guides users through the initial setup process, allowing them to choose between generating a new seed or importing an existing one. This command also handles the encryption of the seed and the creation of a profile.
|
||||
|
||||
**Usage Example:**
|
||||
```bash
|
||||
@@ -587,7 +587,7 @@ seedpass setup
|
||||
**Features to Implement:**
|
||||
- **Seed Choice Prompt:** Asks users whether they want to generate a new seed or import an existing one.
|
||||
- **Encryption of Seed:** Uses the user-selected password to encrypt the seed, whether generated or imported.
|
||||
- **Nostr Profile Creation:** Upon first login, automatically generates a Nostr profile and checks for existing index data notes that can be pulled and decrypted.
|
||||
- **Profile Creation:** Upon first login, automatically generates a profile and checks for existing index data notes that can be pulled and decrypted.
|
||||
|
||||
---
|
||||
|
||||
@@ -604,7 +604,7 @@ seedpass setup
|
||||
- **Features to Implement:**
|
||||
- **Seed Choice Prompt:** Ask users whether they want to generate a new seed or import an existing one.
|
||||
- **Encryption of Seed:** Use the user-selected password to encrypt the seed, whether generated or imported.
|
||||
- **Nostr Profile Creation:** Upon first login, automatically generate a Nostr profile and check for existing index data notes that can be pulled and decrypted.
|
||||
- **Profile Creation:** Upon first login, automatically generate a profile and check for existing index data notes that can be pulled and decrypted.
|
||||
- **Usage Example:** `seedpass setup`
|
||||
|
||||
3. **Advanced CLI Enhancements:**
|
||||
@@ -618,8 +618,8 @@ seedpass setup
|
||||
- **Description:** When running `seedpass setup`, prompts users to either enter an existing seed or generate a new one, followed by password creation for encryption.
|
||||
- **Usage Example:** `seedpass setup`
|
||||
|
||||
- **Automatic Nostr Profile Generation and Index Retrieval:**
|
||||
- **Description:** During the initial setup or first login, generates a Nostr profile and attempts to retrieve and decrypt any existing index data from Nostr.
|
||||
- **Automatic Profile Generation and Index Retrieval:**
|
||||
- **Description:** During the initial setup or first login, generates a profile and attempts to retrieve and decrypt any existing index data from Nostr.
|
||||
- **Usage Example:** `seedpass setup` (handles internally)
|
||||
|
||||
---
|
||||
@@ -632,13 +632,13 @@ seedpass setup
|
||||
```
|
||||
|
||||
- **Consistent Flag Usage:** Use either the short flag or the long flag as per your preference, but maintain consistency for readability.
|
||||
|
||||
|
||||
- **Security Considerations:**
|
||||
- Always use strong, unique master passwords.
|
||||
- Regularly back up your encrypted index.
|
||||
- Enable auto-lock to enhance security.
|
||||
- Be cautious when using the `export` and `import` commands to handle sensitive data securely.
|
||||
|
||||
|
||||
- **Nostr Integration:**
|
||||
- Ensure that your Nostr relays are reliable and secure.
|
||||
- Regularly verify your Nostr public key and manage relays through the `set-relays` command.
|
||||
|
@@ -1,3 +1,7 @@
|
||||
Certainly! Below is the updated **SeedPass JSON Entry Management and Extensibility** documentation tailored to align with the new **Fingerprint-Based Backup and Local Storage** approach. This revision ensures that fingerprints are treated as distinct entities within the JSON schema and directory structures, without referencing any SeedSigner devices.
|
||||
|
||||
---
|
||||
|
||||
# SeedPass JSON Entry Management and Extensibility
|
||||
|
||||
## Table of Contents
|
||||
@@ -29,7 +33,7 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
**SeedPass** is a secure password generator and manager leveraging **Bitcoin's BIP-85 standard** and integrating with the **Nostr network** for decentralized synchronization. To enhance modularity, scalability, and security, SeedPass now manages each password or data entry as a separate JSON file. This document outlines the new entry management system, ensuring that new `kind` types can be added seamlessly without disrupting existing functionalities.
|
||||
**SeedPass** is a secure password generator and manager leveraging **Bitcoin's BIP-85 standard** and integrating with the **Nostr network** for decentralized synchronization. To enhance modularity, scalability, and security, SeedPass now manages each password or data entry as a separate JSON file within a **Fingerprint-Based Backup and Local Storage** system. This document outlines the new entry management system, ensuring that new `kind` types can be added seamlessly without disrupting existing functionalities.
|
||||
|
||||
---
|
||||
|
||||
@@ -43,6 +47,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 0,
|
||||
"index_num": 0,
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "generated_password",
|
||||
"data": {
|
||||
// Fields specific to the kind
|
||||
@@ -64,6 +69,8 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
- For `generated_password` kind: Starts from 0 and increments sequentially.
|
||||
- For other kinds: A secure random hexadecimal string (e.g., a hash of the content) used as the BIP-85 index.
|
||||
|
||||
- **fingerprint** (`string`): A unique identifier generated from the seed associated with the entry. This fingerprint ensures that each seed's data is isolated and securely managed.
|
||||
|
||||
- **kind** (`string`): Specifies the type of entry. Initial kinds include:
|
||||
- `generated_password`
|
||||
- `stored_password`
|
||||
@@ -89,6 +96,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 0,
|
||||
"index_num": 0,
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "generated_password",
|
||||
"data": {
|
||||
"title": "Example Website",
|
||||
@@ -111,7 +119,8 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
```json
|
||||
{
|
||||
"entry_num": 1,
|
||||
"index_num": 1,
|
||||
"index_num": "q1wec4d426fs",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "stored_password",
|
||||
"data": {
|
||||
"title": "Another Service",
|
||||
@@ -133,6 +142,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 2,
|
||||
"index_num": "a1b2c3d4e5f6",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "managed_user",
|
||||
"data": {
|
||||
"users_password": "<encrypted_users_password>"
|
||||
@@ -152,6 +162,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 3,
|
||||
"index_num": "f7g8h9i0j1k2",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "12_word_seed",
|
||||
"data": {
|
||||
"seed_phrase": "<encrypted_seed_phrase>"
|
||||
@@ -171,6 +182,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 4,
|
||||
"index_num": "l3m4n5o6p7q8",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "nostr_keys",
|
||||
"data": {
|
||||
"public_key": "<public_key>",
|
||||
@@ -191,6 +203,7 @@ Each SeedPass entry is stored as an individual JSON file, promoting isolated man
|
||||
{
|
||||
"entry_num": 5,
|
||||
"index_num": "r9s0t1u2v3w4",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "note",
|
||||
"data": {
|
||||
"content": "This is a secure note.",
|
||||
@@ -221,6 +234,7 @@ Each entry is encapsulated in its own JSON file with a standardized structure:
|
||||
{
|
||||
"entry_num": 0,
|
||||
"index_num": 0,
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "generated_password",
|
||||
"data": {
|
||||
// Fields specific to the kind
|
||||
@@ -272,11 +286,12 @@ To maintain compatibility as new `kind` types are introduced, implement the foll
|
||||
def process_entry(entry):
|
||||
kind = entry.get("kind")
|
||||
data = entry.get("data")
|
||||
fingerprint = entry.get("fingerprint")
|
||||
|
||||
if kind == "generated_password":
|
||||
handle_generated_password(data)
|
||||
handle_generated_password(data, fingerprint)
|
||||
elif kind == "stored_password":
|
||||
handle_stored_password(data)
|
||||
handle_stored_password(data, fingerprint)
|
||||
# ... other known kinds ...
|
||||
else:
|
||||
log_warning(f"Unknown kind: {kind}. Skipping entry.")
|
||||
@@ -314,13 +329,13 @@ To ensure seamless integration of new `kind` types in the future, consider the f
|
||||
```python
|
||||
# handlers.py
|
||||
|
||||
def handle_generated_password(data):
|
||||
def handle_generated_password(data, fingerprint):
|
||||
# Implementation
|
||||
|
||||
def handle_stored_password(data):
|
||||
def handle_stored_password(data, fingerprint):
|
||||
# Implementation
|
||||
|
||||
def handle_cryptocurrency_wallet(data):
|
||||
def handle_cryptocurrency_wallet(data, fingerprint):
|
||||
# Implementation
|
||||
```
|
||||
|
||||
@@ -331,7 +346,7 @@ def handle_cryptocurrency_wallet(data):
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
def handle_cryptocurrency_wallet(data):
|
||||
def handle_cryptocurrency_wallet(data, fingerprint):
|
||||
required_fields = ["wallet_name", "address", "private_key"]
|
||||
for field in required_fields:
|
||||
if field not in data:
|
||||
@@ -360,6 +375,7 @@ Create a JSON file following the standardized structure with the new `kind` valu
|
||||
{
|
||||
"entry_num": 6,
|
||||
"index_num": "x1y2z3a4b5c6",
|
||||
"fingerprint": "a1b2c3d4",
|
||||
"kind": "cryptocurrency_wallet",
|
||||
"data": {
|
||||
"wallet_name": "My Bitcoin Wallet",
|
||||
@@ -379,7 +395,7 @@ Create a JSON file following the standardized structure with the new `kind` valu
|
||||
|
||||
**Implement Handler Function:**
|
||||
```python
|
||||
def handle_cryptocurrency_wallet(data):
|
||||
def handle_cryptocurrency_wallet(data, fingerprint):
|
||||
wallet_name = data.get("wallet_name")
|
||||
address = data.get("address")
|
||||
private_key = decrypt(data.get("private_key"))
|
||||
@@ -392,13 +408,14 @@ def handle_cryptocurrency_wallet(data):
|
||||
def process_entry(entry):
|
||||
kind = entry.get("kind")
|
||||
data = entry.get("data")
|
||||
fingerprint = entry.get("fingerprint")
|
||||
|
||||
if kind == "generated_password":
|
||||
handle_generated_password(data)
|
||||
handle_generated_password(data, fingerprint)
|
||||
elif kind == "stored_password":
|
||||
handle_stored_password(data)
|
||||
handle_stored_password(data, fingerprint)
|
||||
elif kind == "cryptocurrency_wallet":
|
||||
handle_cryptocurrency_wallet(data)
|
||||
handle_cryptocurrency_wallet(data, fingerprint)
|
||||
# ... other known kinds ...
|
||||
else:
|
||||
log_warning(f"Unknown kind: {kind}. Skipping entry.")
|
||||
@@ -412,32 +429,50 @@ Existing kinds such as `generated_password`, `stored_password`, etc., continue t
|
||||
|
||||
## Backup and Rollback Mechanism
|
||||
|
||||
To ensure data integrity and provide recovery options, SeedPass implements a robust backup and rollback system.
|
||||
To ensure data integrity and provide recovery options, SeedPass implements a robust backup and rollback system within the **Fingerprint-Based Backup and Local Storage** framework.
|
||||
|
||||
### Backup Directory Structure
|
||||
|
||||
- **Primary Entries Directory:** Stores individual JSON files for each entry.
|
||||
- **Backups Directory:** Stores previous versions of each entry.
|
||||
All backups are organized based on fingerprints, ensuring that each seed's data remains isolated and secure.
|
||||
|
||||
**Example Structure:**
|
||||
```
|
||||
SeedPass/
|
||||
├── entries/
|
||||
│ ├── entry_0.json
|
||||
│ ├── entry_1.json
|
||||
│ └── ...
|
||||
├── backups/
|
||||
│ ├── entry_0_v1.json
|
||||
│ ├── entry_0_v2.json
|
||||
│ ├── entry_1_v1.json
|
||||
│ └── ...
|
||||
~/.seedpass/
|
||||
├── a1b2c3d4/
|
||||
│ ├── entries/
|
||||
│ │ ├── entry_0.json
|
||||
│ │ ├── entry_1.json
|
||||
│ │ └── ...
|
||||
│ ├── backups/
|
||||
│ │ ├── entry_0_v1.json
|
||||
│ │ ├── entry_0_v2.json
|
||||
│ │ ├── entry_1_v1.json
|
||||
│ │ └── ...
|
||||
│ ├── parent_seed.enc
|
||||
│ ├── seedpass_passwords_checksum.txt
|
||||
│ ├── seedpass_passwords_db_checksum.txt
|
||||
│ └── seedpass_passwords_db.json
|
||||
├── b5c6d7e8/
|
||||
│ ├── entries/
|
||||
│ │ ├── entry_0.json
|
||||
│ │ ├── entry_1.json
|
||||
│ │ └── ...
|
||||
│ ├── backups/
|
||||
│ │ ├── entry_0_v1.json
|
||||
│ │ ├── entry_0_v2.json
|
||||
│ │ ├── entry_1_v1.json
|
||||
│ │ └── ...
|
||||
│ ├── parent_seed.enc
|
||||
│ ├── seedpass_passwords_checksum.txt
|
||||
│ ├── seedpass_passwords_db_checksum.txt
|
||||
│ └── seedpass_passwords_db.json
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Backup Process
|
||||
|
||||
1. **Upon Modifying an Entry:**
|
||||
- The current version of the entry is copied to the `backups/` directory with a version suffix (e.g., `entry_0_v1.json`).
|
||||
- The modified entry is saved in the `entries/` directory.
|
||||
- The current version of the entry is copied to the `backups/` directory within the corresponding fingerprint folder with a version suffix (e.g., `entry_0_v1.json`).
|
||||
- The modified entry is saved in the `entries/` directory within the same fingerprint folder.
|
||||
|
||||
2. **Versioning:**
|
||||
- Each backup file includes a version number to track changes over time.
|
||||
@@ -445,10 +480,32 @@ SeedPass/
|
||||
### Rollback Functionality
|
||||
|
||||
- **Restoring an Entry:**
|
||||
- Users can select a backup version from the `backups/` directory.
|
||||
- Users can select a backup version from the `backups/` directory within the specific fingerprint folder.
|
||||
- The selected backup file is copied back to the `entries/` directory, replacing the current version.
|
||||
|
||||
**Example Command:**
|
||||
```bash
|
||||
python main.py rollback entry_0_v1.json
|
||||
```
|
||||
seedpass rollback --fingerprint a1b2c3d4 --file entry_0_v1.json
|
||||
```
|
||||
|
||||
**Example Directory Structure After Rollback:**
|
||||
```
|
||||
~/.seedpass/
|
||||
├── a1b2c3d4/
|
||||
│ ├── entries/
|
||||
│ │ ├── entry_0.json # Restored from entry_0_v1.json
|
||||
│ │ ├── entry_1.json
|
||||
│ │ └── ...
|
||||
│ ├── backups/
|
||||
│ │ ├── entry_0_v1.json
|
||||
│ │ ├── entry_0_v2.json
|
||||
│ │ ├── entry_1_v1.json
|
||||
│ │ └── ...
|
||||
│ ├── parent_seed.enc
|
||||
│ ├── seedpass_passwords_checksum.txt
|
||||
│ ├── seedpass_passwords_db_checksum.txt
|
||||
│ └── seedpass_passwords_db.json
|
||||
├── ...
|
||||
```
|
||||
|
||||
*Note: Restoring a backup overwrites the current entry. Ensure that you intend to revert to the selected backup before proceeding.*
|
@@ -91,3 +91,5 @@ MAX_PASSWORD_LENGTH = 128 # Maximum allowed password length
|
||||
# Additional Constants (if any)
|
||||
# -----------------------------------
|
||||
# Add any other constants here as your project expands
|
||||
HASHED_PASSWORD_FILE = APP_DIR / 'hashed_password.enc'
|
||||
DEFAULT_SEED_BACKUP_FILENAME = 'parent_seed_backup.enc'
|
||||
|
@@ -57,11 +57,12 @@ def display_menu(password_manager: PasswordManager, nostr_client: NostrClient):
|
||||
5. Post Encrypted Index to Nostr
|
||||
6. Retrieve Encrypted Index from Nostr
|
||||
7. Display Nostr Public Key (npub)
|
||||
8. Exit
|
||||
8. Backup/Reveal Parent Seed
|
||||
9. Exit
|
||||
"""
|
||||
while True:
|
||||
print(colored(menu, 'cyan'))
|
||||
choice = input('Enter your choice (1-8): ').strip()
|
||||
choice = input('Enter your choice (1-9): ').strip() # Updated to include option 9
|
||||
if choice == '1':
|
||||
password_manager.handle_generate_password()
|
||||
elif choice == '2':
|
||||
@@ -77,6 +78,8 @@ def display_menu(password_manager: PasswordManager, nostr_client: NostrClient):
|
||||
elif choice == '7':
|
||||
handle_display_npub(nostr_client)
|
||||
elif choice == '8':
|
||||
password_manager.handle_backup_reveal_parent_seed() # Corrected variable name
|
||||
elif choice == '9':
|
||||
logging.info("Exiting the program.")
|
||||
print(colored("Exiting the program.", 'green'))
|
||||
nostr_client.close_client_pool() # Gracefully close the ClientPool
|
||||
|
@@ -16,6 +16,7 @@ This means it should generate passwords the exact same way every single time. S
|
||||
|
||||
import os
|
||||
import json
|
||||
import stat
|
||||
import hashlib
|
||||
import logging
|
||||
import traceback
|
||||
@@ -90,16 +91,18 @@ class EncryptionManager:
|
||||
|
||||
def encrypt_parent_seed(self, parent_seed: str, file_path: Path) -> None:
|
||||
"""
|
||||
Encrypts and saves the parent seed to the specified file.
|
||||
Encrypts and securely saves the parent seed to the specified file.
|
||||
|
||||
:param parent_seed: The BIP39 parent seed phrase.
|
||||
:param file_path: The path to the file where the encrypted parent seed will be saved.
|
||||
"""
|
||||
try:
|
||||
# **Do not encrypt the data here**
|
||||
# Encode the parent seed to bytes
|
||||
data = parent_seed.encode('utf-8')
|
||||
# Pass the raw data to encrypt_file, which handles encryption
|
||||
# Encrypt and write to file using encrypt_file
|
||||
self.encrypt_file(file_path, data)
|
||||
# Set file permissions to read/write for the user only
|
||||
os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR)
|
||||
logger.info(f"Parent seed encrypted and saved to '{file_path}'.")
|
||||
print(colored(f"Parent seed encrypted and saved to '{file_path}'.", 'green'))
|
||||
except Exception as e:
|
||||
@@ -141,7 +144,7 @@ class EncryptionManager:
|
||||
return encrypted_data
|
||||
except Exception as e:
|
||||
logger.error(f"Error encrypting data: {e}")
|
||||
logger.error(traceback.format_exc()) # Log full traceback
|
||||
logger.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to encrypt data: {e}", 'red'))
|
||||
raise
|
||||
|
||||
|
@@ -25,7 +25,7 @@ from password_manager.password_generation import PasswordGenerator
|
||||
from password_manager.backup import BackupManager
|
||||
from utils.key_derivation import derive_key_from_parent_seed, derive_key_from_password
|
||||
from utils.checksum import calculate_checksum, verify_checksum
|
||||
from utils.password_prompt import prompt_for_password
|
||||
from utils.password_prompt import prompt_for_password, prompt_existing_password, confirm_action
|
||||
|
||||
from constants import (
|
||||
APP_DIR,
|
||||
@@ -35,10 +35,14 @@ from constants import (
|
||||
SCRIPT_CHECKSUM_FILE,
|
||||
MIN_PASSWORD_LENGTH,
|
||||
MAX_PASSWORD_LENGTH,
|
||||
DEFAULT_PASSWORD_LENGTH
|
||||
DEFAULT_PASSWORD_LENGTH,
|
||||
HASHED_PASSWORD_FILE, # Ensure this constant is defined in constants.py
|
||||
DEFAULT_SEED_BACKUP_FILENAME
|
||||
)
|
||||
|
||||
import traceback # Added for exception traceback logging
|
||||
import bcrypt # Ensure bcrypt is installed in your environment
|
||||
from pathlib import Path # Required for handling file paths
|
||||
|
||||
# Configure logging at the start of the module
|
||||
def configure_logging():
|
||||
@@ -112,7 +116,7 @@ class PasswordManager:
|
||||
self.encryption_manager = EncryptionManager(key)
|
||||
self.parent_seed = self.encryption_manager.decrypt_parent_seed(PARENT_SEED_FILE)
|
||||
|
||||
# **Add validation for the decrypted seed**
|
||||
# Validate the decrypted seed
|
||||
if not self.validate_seed_phrase(self.parent_seed):
|
||||
logging.error("Decrypted seed is invalid. Exiting.")
|
||||
print(colored("Error: Decrypted seed is invalid.", 'red'))
|
||||
@@ -149,6 +153,9 @@ class PasswordManager:
|
||||
try:
|
||||
self.encryption_manager.encrypt_parent_seed(parent_seed, PARENT_SEED_FILE)
|
||||
logging.info("Parent seed encrypted and saved successfully.")
|
||||
# Store the hashed password
|
||||
self.store_hashed_password(password)
|
||||
logging.info("User password hashed and stored successfully.")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to encrypt and save parent seed: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
@@ -182,7 +189,7 @@ class PasswordManager:
|
||||
print(colored(f"Error: {e}", 'red'))
|
||||
return None
|
||||
|
||||
def validate_seed_phrase(self, seed_phrase: str) -> Optional[str]:
|
||||
def validate_seed_phrase(self, seed_phrase: str) -> bool:
|
||||
"""
|
||||
Validates the seed phrase using the EncryptionManager if available,
|
||||
otherwise performs basic validation.
|
||||
@@ -191,26 +198,26 @@ class PasswordManager:
|
||||
seed_phrase (str): The seed phrase to validate.
|
||||
|
||||
Returns:
|
||||
Optional[str]: The validated seed phrase or None if invalid.
|
||||
bool: True if valid, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if self.encryption_manager:
|
||||
# Use EncryptionManager to validate seed
|
||||
if self.encryption_manager.validate_seed(seed_phrase):
|
||||
is_valid = self.encryption_manager.validate_seed(seed_phrase)
|
||||
if is_valid:
|
||||
logging.debug("Seed phrase validated successfully using EncryptionManager.")
|
||||
return seed_phrase
|
||||
else:
|
||||
logging.error("Invalid seed phrase.")
|
||||
print(colored("Error: Invalid seed phrase.", 'red'))
|
||||
return None
|
||||
return is_valid
|
||||
else:
|
||||
# Perform basic validation
|
||||
return self.basic_validate_seed_phrase(seed_phrase)
|
||||
return self.basic_validate_seed_phrase(seed_phrase) is not None
|
||||
except Exception as e:
|
||||
logging.error(f"Error validating seed phrase: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to validate seed phrase: {e}", 'red'))
|
||||
return None
|
||||
return False
|
||||
|
||||
def initialize_managers(self) -> None:
|
||||
"""
|
||||
@@ -443,7 +450,101 @@ class PasswordManager:
|
||||
logging.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to restore backup: {e}", 'red'))
|
||||
|
||||
# Additional methods can be added here as needed
|
||||
def handle_backup_reveal_parent_seed(self) -> None:
|
||||
"""
|
||||
Handles the backup and reveal of the parent seed.
|
||||
"""
|
||||
try:
|
||||
print(colored("\n=== Backup/Reveal Parent Seed ===", 'yellow'))
|
||||
print(colored("Warning: Revealing your parent seed is a highly sensitive operation.", 'red'))
|
||||
print(colored("Ensure you're in a secure, private environment and no one is watching your screen.", 'red'))
|
||||
|
||||
# Verify user's identity with secure password verification
|
||||
password = prompt_existing_password("Enter your master password to continue: ")
|
||||
if not self.verify_password(password):
|
||||
print(colored("Incorrect password. Operation aborted.", 'red'))
|
||||
return
|
||||
|
||||
# Double confirmation
|
||||
if not confirm_action("Are you absolutely sure you want to reveal your parent seed? (Y/N): "):
|
||||
print(colored("Operation cancelled by user.", 'yellow'))
|
||||
return
|
||||
|
||||
# Reveal the parent seed
|
||||
print(colored("\n=== Your Parent Seed ===", 'green'))
|
||||
print(colored(self.parent_seed, 'yellow'))
|
||||
print(colored("\nPlease write this down and store it securely. Do not share it with anyone.", 'red'))
|
||||
|
||||
# Option to save to file with default filename
|
||||
if confirm_action("Do you want to save this to an encrypted backup file? (Y/N): "):
|
||||
filename = input(f"Enter filename to save (default: {DEFAULT_SEED_BACKUP_FILENAME}): ").strip()
|
||||
filename = filename if filename else DEFAULT_SEED_BACKUP_FILENAME
|
||||
backup_path = Path(APP_DIR) / filename
|
||||
|
||||
# Validate filename
|
||||
if not self.is_valid_filename(filename):
|
||||
print(colored("Invalid filename. Operation aborted.", 'red'))
|
||||
return
|
||||
|
||||
self.encryption_manager.encrypt_parent_seed(self.parent_seed, backup_path)
|
||||
print(colored(f"Encrypted seed backup saved to '{backup_path}'. Ensure this file is stored securely.", 'green'))
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error during parent seed backup/reveal: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to backup/reveal parent seed: {e}", 'red'))
|
||||
|
||||
def verify_password(self, password: str) -> bool:
|
||||
"""
|
||||
Verifies the provided password against the stored hashed password.
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(HASHED_PASSWORD_FILE):
|
||||
logging.error("Hashed password file not found.")
|
||||
print(colored("Error: Hashed password file not found.", 'red'))
|
||||
return False
|
||||
with open(HASHED_PASSWORD_FILE, 'rb') as f:
|
||||
stored_hash = f.read()
|
||||
is_correct = bcrypt.checkpw(password.encode('utf-8'), stored_hash)
|
||||
if is_correct:
|
||||
logging.debug("Password verification successful.")
|
||||
else:
|
||||
logging.warning("Password verification failed.")
|
||||
return is_correct
|
||||
except Exception as e:
|
||||
logging.error(f"Error verifying password: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to verify password: {e}", 'red'))
|
||||
return False
|
||||
|
||||
def is_valid_filename(self, filename: str) -> bool:
|
||||
"""
|
||||
Validates the provided filename to prevent directory traversal and invalid characters.
|
||||
"""
|
||||
# Basic validation: filename should not contain path separators or be empty
|
||||
invalid_chars = ['/', '\\', '..']
|
||||
if any(char in filename for char in invalid_chars) or not filename:
|
||||
logging.warning(f"Invalid filename attempted: {filename}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def store_hashed_password(self, password: str) -> None:
|
||||
"""
|
||||
Hashes and stores the user's password securely using bcrypt.
|
||||
This should be called during the initial setup.
|
||||
"""
|
||||
try:
|
||||
hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
||||
with open(HASHED_PASSWORD_FILE, 'wb') as f:
|
||||
f.write(hashed)
|
||||
# Set file permissions to read/write for the user only
|
||||
os.chmod(HASHED_PASSWORD_FILE, 0o600)
|
||||
logging.info("User password hashed and stored successfully.")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to store hashed password: {e}")
|
||||
logging.error(traceback.format_exc())
|
||||
print(colored(f"Error: Failed to store hashed password: {e}", 'red'))
|
||||
raise
|
||||
|
||||
# Example usage (this part should be removed or commented out when integrating into the larger application)
|
||||
if __name__ == "__main__":
|
||||
|
@@ -5,4 +5,5 @@ bip-utils>=2.5.0
|
||||
bech32==1.2.0
|
||||
monstr @ git+https://github.com/monty888/monstr.git@master#egg=monstr
|
||||
mnemonic
|
||||
aiohttp
|
||||
aiohttp
|
||||
bcrypt
|
Reference in New Issue
Block a user