Add main CLI entry point and full test suite

- bigbrother.py: Click CLI with start/stop/status/activate/deactivate/
  kill/config/selftest/modules commands plus Rich interactive menu.
  Module discovery scans modules/stealth/ for BaseModule subclasses.
  Supports --daemon mode with PID file and --passive-only flag.
- tests/conftest.py: Shared fixtures (tmp_dir, mock_config, mock_bus,
  mock_state, hardware_tier_override, project_root)
- tests/test_bus.py: 6 tests for EventBus pub/sub, filtering, wildcards
- tests/test_engine.py: 5 tests for Engine lifecycle, deps, tier, phase
- tests/test_crypto.py: 5 tests for AES-256-GCM, file encryption, KDF
- tests/test_resource.py: 7 tests for hardware detection, memory, disk
- tests/test_tool_manager.py: 6 tests for subprocess management
- tests/test_stealth_modules.py: 4 parametrized test classes covering
  all 12 stealth modules (import, inheritance, attributes, instantiation)
This commit is contained in:
n0mad1k
2026-03-18 09:48:23 -04:00
parent 6caf1a15ca
commit 3394c72814
9 changed files with 1516 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
"""Tests for utils.crypto — AES-256-GCM encryption and key derivation."""
import os
import tempfile
import pytest
from utils.crypto import (
CryptoEngine,
KEY_SIZE,
NONCE_SIZE,
derive_key,
encrypt_file,
decrypt_file,
HAS_ARGON2,
)
class TestCryptoEngine:
def test_encrypt_decrypt_roundtrip(self):
"""Encrypt then decrypt returns the original plaintext."""
key = os.urandom(KEY_SIZE)
engine = CryptoEngine(key)
plaintext = b"Sensitive credential data: admin:p@ssw0rd!"
ciphertext = engine.encrypt(plaintext)
# Ciphertext should differ from plaintext
assert ciphertext != plaintext
# Should include nonce prefix
assert len(ciphertext) > len(plaintext)
decrypted = engine.decrypt(ciphertext)
assert decrypted == plaintext
def test_encrypt_file_decrypt_file(self, tmp_path):
"""encrypt_file() and decrypt_file() round-trip a file correctly."""
src = str(tmp_path / "source.bin")
enc = str(tmp_path / "encrypted.bb")
dec = str(tmp_path / "decrypted.bin")
original_data = os.urandom(4096)
with open(src, "wb") as f:
f.write(original_data)
password = b"test-passphrase-42"
# Use pbkdf2 to avoid argon2 dependency issues
encrypt_file(src, enc, password, method="pbkdf2")
decrypt_file(enc, dec, password, method="pbkdf2")
with open(dec, "rb") as f:
recovered = f.read()
assert recovered == original_data
def test_different_keys_fail(self):
"""Decrypting with a different key raises an error."""
key1 = os.urandom(KEY_SIZE)
key2 = os.urandom(KEY_SIZE)
engine1 = CryptoEngine(key1)
engine2 = CryptoEngine(key2)
plaintext = b"secret data"
ciphertext = engine1.encrypt(plaintext)
with pytest.raises(Exception):
engine2.decrypt(ciphertext)
def test_key_derivation_argon2(self):
"""derive_key with argon2id returns a 32-byte key (or falls back to pbkdf2)."""
password = b"my-strong-password"
salt = os.urandom(16)
key = derive_key(password, salt, method="argon2id")
assert isinstance(key, bytes)
assert len(key) == KEY_SIZE
# Same inputs produce the same key
key2 = derive_key(password, salt, method="argon2id")
assert key == key2
# Different salt produces a different key
salt2 = os.urandom(16)
key3 = derive_key(password, salt2, method="argon2id")
assert key3 != key
def test_key_derivation_pbkdf2(self):
"""derive_key with pbkdf2 returns a 32-byte key."""
password = b"another-password"
salt = os.urandom(16)
key = derive_key(password, salt, method="pbkdf2")
assert isinstance(key, bytes)
assert len(key) == KEY_SIZE
# Same inputs produce the same key
key2 = derive_key(password, salt, method="pbkdf2")
assert key == key2
# Different password produces a different key
key3 = derive_key(b"different-password", salt, method="pbkdf2")
assert key3 != key