diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-09 04:05:16 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-09 04:05:16 +0200 |
| commit | 6b6b1a9d2febaa75da7609db671a82a458b40b4b (patch) | |
| tree | 4865497ae89be70aed4f83e81f059d2a6f56a0e3 /packages/meshbay-node/tests/test_keystore.py | |
| parent | e3af7d272bec0bdc426bdad16ad0ad2f203e8113 (diff) | |
| download | meshbay-6b6b1a9d2febaa75da7609db671a82a458b40b4b.tar.gz | |
feat(node): add keystore module with 3 unlock modes
Argon2id + AES-256-GCM encryption at rest. Unlock via env var
(MESHBAY_UNLOCK_KEY), unlock.key file (chmod 600), or interactive
getpass. 10/10 tests passing.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_keystore.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_keystore.py | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_keystore.py b/packages/meshbay-node/tests/test_keystore.py new file mode 100644 index 0000000..b3c2b56 --- /dev/null +++ b/packages/meshbay-node/tests/test_keystore.py @@ -0,0 +1,100 @@ +"""Tests for meshbay_node.keystore.""" + +import pytest +from pathlib import Path +from meshbay_node.keystore import ( + NodeKeys, + create_keystore, + load_keystore, + save_keystore, + load_or_create_keystore, +) +from meshbay_common.crypto import generate_gek + + +def test_create_and_load(tmp_path): + path = tmp_path / "keystore.enc" + keys = create_keystore(path=path, password="testpass99") + + assert keys.sk_ed25519 is not None + assert keys.sk_x25519 is not None + assert keys.gek is None + assert len(keys.pk_ed25519_b64) == 44 # 32 bytes → 44 base64 chars + assert len(keys.pk_x25519_b64) == 44 + assert path.exists() + assert oct(path.stat().st_mode)[-3:] == "600" + + loaded = load_keystore(path=path, password="testpass99") + assert loaded.pk_ed25519_b64 == keys.pk_ed25519_b64 + assert loaded.pk_x25519_b64 == keys.pk_x25519_b64 + assert loaded.gek is None + + +def test_wrong_password_rejected(tmp_path): + path = tmp_path / "keystore.enc" + create_keystore(path=path, password="correctpass") + with pytest.raises(ValueError, match="Wrong password"): + load_keystore(path=path, password="wrongpass") + + +def test_create_fails_if_exists(tmp_path): + path = tmp_path / "keystore.enc" + create_keystore(path=path, password="pass12345") + with pytest.raises(FileExistsError): + create_keystore(path=path, password="pass12345") + + +def test_load_fails_if_missing(tmp_path): + with pytest.raises(FileNotFoundError): + load_keystore(path=tmp_path / "nonexistent.enc", password="x") + + +def test_save_with_gek(tmp_path): + path = tmp_path / "keystore.enc" + keys = create_keystore(path=path, password="testpass99") + gek = generate_gek() + keys.gek = gek + + save_keystore(keys, path=path, password="testpass99") + + reloaded = load_keystore(path=path, password="testpass99") + assert reloaded.gek == gek + + +def test_load_or_create_creates(tmp_path): + path = tmp_path / "keystore.enc" + keys = load_or_create_keystore(path=path, password="testpass99") + assert keys.sk_ed25519 is not None + assert path.exists() + + +def test_load_or_create_loads(tmp_path): + path = tmp_path / "keystore.enc" + k1 = load_or_create_keystore(path=path, password="testpass99") + k2 = load_or_create_keystore(path=path, password="testpass99") + assert k1.pk_ed25519_b64 == k2.pk_ed25519_b64 + + +def test_keys_unique_per_creation(tmp_path): + k1 = create_keystore(path=tmp_path / "k1.enc", password="p1234567") + k2 = create_keystore(path=tmp_path / "k2.enc", password="p1234567") + assert k1.pk_ed25519_b64 != k2.pk_ed25519_b64 + + +def test_env_var_unlock(tmp_path, monkeypatch): + path = tmp_path / "keystore.enc" + create_keystore(path=path, password="envpass42") + monkeypatch.setenv("MESHBAY_UNLOCK_KEY", "envpass42") + keys = load_keystore(path=path) # no password arg + assert keys.sk_ed25519 is not None + + +def test_unlock_file(tmp_path): + path = tmp_path / "keystore.enc" + kf_path = tmp_path / "unlock.key" + kf_path.write_text("filepass42") + kf_path.chmod(0o600) + + create_keystore(path=path, password="filepass42") + keys = load_keystore(path=path, unlock_file=kf_path) # no password arg + assert keys.sk_ed25519 is not None |