Encrypt remote transport with post-quantum session keys
This commit is contained in:
+51
-2
@@ -6,7 +6,7 @@ import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from browser_cli.auth import generate_keypair, load_private_key, new_nonce, sign
|
||||
from browser_cli.auth import generate_keypair, load_private_key, new_nonce, pq_decrypt, pq_encrypt, sign
|
||||
from browser_cli.client import BrowserNotConnected
|
||||
from browser_cli.commands.serve import _handle_client
|
||||
|
||||
@@ -167,7 +167,7 @@ class TestRejection:
|
||||
|
||||
client, t, challenge = self._connect(path)
|
||||
nonce = bytes.fromhex(challenge["nonce"])
|
||||
msg = {"id": "x", "command": "tabs.list", "args": {}, "user_agent": "browser-cli/0.9.4", "pubkey": pub}
|
||||
msg = {"id": "x", "command": "tabs.list", "args": {}, "user_agent": "browser-cli/0.9.5", "pubkey": pub}
|
||||
msg["sig"] = sign(priv, nonce, msg).hex()
|
||||
_send_framed(client, json.dumps(msg).encode())
|
||||
resp = _recv_framed(client)
|
||||
@@ -302,6 +302,55 @@ class TestAuthSuccess:
|
||||
client.close()
|
||||
t.join(timeout=2)
|
||||
|
||||
def test_post_quantum_encrypted_transport_reaches_proxy(self, tmp_path, monkeypatch):
|
||||
"""New clients encrypt the command payload and receive encrypted responses."""
|
||||
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
|
||||
monkeypatch.setattr("browser_cli.auth.pq_kex_server_keypair", lambda: ("fake-private", b"fake-public"))
|
||||
monkeypatch.setattr("browser_cli.auth.pq_kex_server_decapsulate", lambda priv, ct: b"pq-secret")
|
||||
|
||||
path = tmp_path / "authorized_keys"
|
||||
pem, pub = generate_keypair()
|
||||
path.write_text(pub + "\n")
|
||||
key_path = tmp_path / "client.key.pem"
|
||||
key_path.write_bytes(pem)
|
||||
priv = load_private_key(key_path)
|
||||
|
||||
client, server = _pair()
|
||||
t = threading.Thread(
|
||||
target=_handle_client,
|
||||
args=(server, ("127.0.0.1", 9999), None, path),
|
||||
daemon=True,
|
||||
)
|
||||
t.start()
|
||||
|
||||
challenge = _recv_framed(client)
|
||||
nonce = bytes.fromhex(challenge["nonce"])
|
||||
clean_msg = {
|
||||
"id": "x",
|
||||
"command": "tabs.list",
|
||||
"args": {},
|
||||
"user_agent": "browser-cli/0.9.5",
|
||||
"pq_kex": {"alg": "ML-KEM-768", "ciphertext": "cafe"},
|
||||
}
|
||||
sig = sign(priv, nonce, clean_msg, b"pq-secret").hex()
|
||||
envelope = {
|
||||
"id": "x",
|
||||
"user_agent": "browser-cli/0.9.5",
|
||||
"pubkey": pub,
|
||||
"sig": sig,
|
||||
"pq_kex": clean_msg["pq_kex"],
|
||||
"encrypted": pq_encrypt(b"pq-secret", "request", json.dumps(clean_msg).encode()),
|
||||
}
|
||||
_send_framed(client, json.dumps(envelope).encode())
|
||||
encrypted_resp = _recv_framed(client)
|
||||
|
||||
assert "encrypted" in encrypted_resp
|
||||
resp = json.loads(pq_decrypt(b"pq-secret", "response", encrypted_resp["encrypted"]))
|
||||
assert "unauthorized" not in resp.get("error", "").lower()
|
||||
assert "browser" in resp.get("error", "").lower() or "connected" in resp.get("error", "").lower()
|
||||
client.close()
|
||||
t.join(timeout=2)
|
||||
|
||||
def test_no_auth_mode_reaches_proxy(self, monkeypatch):
|
||||
"""auth_keys_path=None (--no-auth): no pubkey required, reaches proxy layer."""
|
||||
monkeypatch.setattr("browser_cli.client._resolve_socket", _mock_no_browser)
|
||||
|
||||
Reference in New Issue
Block a user