From 437a2aa5aaa3b435d39dfe0c0e1d3abf61594d7e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 6 Jul 2025 15:31:14 -0400 Subject: [PATCH] Add script to generate sample SeedPass profile --- scripts/generate_test_profile.py | 126 +++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 scripts/generate_test_profile.py diff --git a/scripts/generate_test_profile.py b/scripts/generate_test_profile.py new file mode 100644 index 0000000..ea5d7ea --- /dev/null +++ b/scripts/generate_test_profile.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""Generate a SeedPass test profile with realistic entries. + +This script populates a profile directory with a variety of entry types. +If the profile does not exist, a new BIP-39 seed phrase is generated and +stored encrypted. A clear text copy is written to ``seed_phrase.txt`` so +it can be reused across devices. +""" + +from __future__ import annotations + +import argparse +import random +from pathlib import Path + +from bip_utils import Bip39MnemonicGenerator, Bip39WordsNum, Bip39Languages + +from constants import APP_DIR +from utils.key_derivation import derive_key_from_password, derive_index_key +from password_manager.encryption import EncryptionManager +from password_manager.vault import Vault +from password_manager.config_manager import ConfigManager +from password_manager.backup import BackupManager +from password_manager.entry_management import EntryManager + +DEFAULT_PASSWORD = "testpassword" + + +def initialize_profile(profile_name: str) -> tuple[str, EntryManager, Path]: + """Create or load a profile and return the seed phrase and manager.""" + profile_dir = APP_DIR / profile_name + profile_dir.mkdir(parents=True, exist_ok=True) + + seed_key = derive_key_from_password(DEFAULT_PASSWORD) + seed_mgr = EncryptionManager(seed_key, profile_dir) + seed_file = profile_dir / "parent_seed.enc" + + if seed_file.exists(): + seed_phrase = seed_mgr.decrypt_parent_seed() + else: + seed_phrase = ( + Bip39MnemonicGenerator(Bip39Languages.ENGLISH) + .FromWordsNumber(Bip39WordsNum.WORDS_NUM_12) + .ToStr() + ) + seed_mgr.encrypt_parent_seed(seed_phrase) + clear_path = profile_dir / "seed_phrase.txt" + clear_path.write_text(seed_phrase) + clear_path.chmod(0o600) + + index_key = derive_index_key(seed_phrase) + enc_mgr = EncryptionManager(index_key, profile_dir) + vault = Vault(enc_mgr, profile_dir) + cfg_mgr = ConfigManager(vault, profile_dir) + backup_mgr = BackupManager(profile_dir, cfg_mgr) + entry_mgr = EntryManager(vault, backup_mgr) + return seed_phrase, entry_mgr, profile_dir + + +def random_secret(length: int = 16) -> str: + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" + return "".join(random.choice(alphabet) for _ in range(length)) + + +def populate(entry_mgr: EntryManager, seed: str, count: int) -> None: + """Add ``count`` entries of varying types to the vault.""" + start_index = entry_mgr.get_next_index() + for i in range(count): + idx = start_index + i + kind = idx % 7 + if kind == 0: + entry_mgr.add_entry( + label=f"site-{idx}.example.com", + length=12, + username=f"user{idx}", + url=f"https://example{idx}.com", + notes=f"Website account {idx}", + custom_fields=[{"key": "id", "value": str(idx)}], + ) + elif kind == 1: + entry_mgr.add_totp(f"totp-generated-{idx}", seed) + elif kind == 2: + entry_mgr.add_totp(f"totp-imported-{idx}", seed, secret=random_secret()) + elif kind == 3: + entry_mgr.add_ssh_key(f"ssh-{idx}", seed, notes=f"SSH key for server {idx}") + elif kind == 4: + entry_mgr.add_seed( + f"derived-seed-{idx}", seed, words_num=24, notes=f"Seed {idx}" + ) + elif kind == 5: + entry_mgr.add_nostr_key(f"nostr-{idx}", notes=f"Nostr key {idx}") + else: + entry_mgr.add_pgp_key( + f"pgp-{idx}", + seed, + user_id=f"user{idx}@example.com", + notes=f"PGP key {idx}", + ) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Create or extend a SeedPass test profile" + ) + parser.add_argument( + "--profile", + default="test_profile", + help="profile name inside ~/.seedpass", + ) + parser.add_argument( + "--count", + type=int, + default=100, + help="number of entries to add", + ) + args = parser.parse_args() + + seed, entry_mgr, dir_path = initialize_profile(args.profile) + print(f"Using profile directory: {dir_path}") + print(f"Parent seed: {seed}") + populate(entry_mgr, seed, args.count) + print(f"Added {args.count} entries.") + + +if __name__ == "__main__": + main()