노트북 04 — CLI를 통한 Alice↔Bob 전체 세션#
inbox 디렉터리를 공유하는 두 개의 실제 CLI 프로세스를 띄우고, 5번의 왕복 메시지를 관찰합니다.
import os, subprocess, tempfile, shutil
from pathlib import Path
workdir = Path(tempfile.mkdtemp(prefix="pqmsg_nb04_"))
alice_home = workdir / "alice"; alice_home.mkdir()
bob_home = workdir / "bob"; bob_home.mkdir()
shared_inbox = workdir / "shared_inbox"; shared_inbox.mkdir()
(alice_home / "inbox").symlink_to(shared_inbox, target_is_directory=True)
(bob_home / "inbox").symlink_to(shared_inbox, target_is_directory=True)
print("workdir:", workdir)
workdir: /tmp/pqmsg_nb04__f_wyd4i
def run(home, *args):
env = os.environ.copy()
env["PQMSG_HOME"] = str(home)
r = subprocess.run(["pqmsg"] + list(args), env=env, capture_output=True, text=True, timeout=60)
return r
print(run(alice_home, "init", "--name", "alice").stdout.strip())
print(run(bob_home, "init", "--name", "bob").stdout.strip())
Generated identity for 'alice' at /tmp/pqmsg_nb04__f_wyd4i/alice/identity.json
Share your contact file: pqmsg export-contact --output /tmp/alice.pub
Generated identity for 'bob' at /tmp/pqmsg_nb04__f_wyd4i/bob/identity.json
Share your contact file: pqmsg export-contact --output /tmp/bob.pub
alice_pub = workdir / "alice.pub"
bob_pub = workdir / "bob.pub"
run(alice_home, "export-contact", "--output", str(alice_pub))
run(bob_home, "export-contact", "--output", str(bob_pub))
run(alice_home, "import-contact", str(bob_pub), "--as", "bob")
run(bob_home, "import-contact", str(alice_pub), "--as", "alice")
print("contacts exchanged")
contacts exchanged
for i in range(5):
msg = f"round {i}: hello from alice"
r = run(alice_home, "send", "bob", msg)
print("ALICE:", r.stdout.strip())
r = run(bob_home, "recv")
print("BOB: ", r.stdout.strip())
ALICE: sent message #0 to bob (1827 bytes)
BOB: [from alice, msg #0]: round 0: hello from alice
ALICE: sent message #1 to bob (335 bytes)
BOB: [from alice, msg #1]: round 1: hello from alice
ALICE: sent message #2 to bob (335 bytes)
BOB: [from alice, msg #2]: round 2: hello from alice
ALICE: sent message #3 to bob (335 bytes)
BOB: [from alice, msg #3]: round 3: hello from alice
ALICE: sent message #4 to bob (335 bytes)
BOB: [from alice, msg #4]: round 4: hello from alice
각 “ALICE” 줄은 Alice→Bob 체인 키를 전진시키고, 각 “BOB” 줄은 Bob의 Alice→Bob 수신 체인을 전진시킵니다. 양쪽이 동기화된 상태로 유지됩니다.#
print("--- Alice session state ---")
print(run(alice_home, "show-keys", "bob").stdout)
print("--- Bob session state ---")
print(run(bob_home, "show-keys", "alice").stdout)
--- Alice session state ---
peer: bob
send_index: 5
recv_index: 0
chain_key_send (first 8 bytes): fb55d2a095a1c42a
chain_key_recv (first 8 bytes): c83dcc2f29d68d19
--- Bob session state ---
peer: alice
send_index: 0
recv_index: 5
chain_key_send (first 8 bytes): c83dcc2f29d68d19
chain_key_recv (first 8 bytes): fb55d2a095a1c42a
shutil.rmtree(workdir, ignore_errors=True)
print("cleaned", workdir)
cleaned /tmp/pqmsg_nb04__f_wyd4i
방금 본 것#
각각 자체
~/.pq-messenger디렉터리를 가진 두 개의 독립적인 OS 프로세스.공유 파일 큐 “네트워크”.
단일 하이브리드 X3DH 핸드셰이크를 앞에 두고, 5번의 암호화/복호화 왕복.
단조 증가하는 송신/수신 인덱스 — 동작 중인 대칭 래칫.
실제 배포에서는 완전한 Double Ratchet(DH 재키잉), 적절한 상호 인증(서명 + 발행된 prekey), 그리고 오프라인 전송을 위한 서버를 원할 것입니다. 이는 다음 단계의 복잡성으로, 의도적으로 독자에게 남겨두었습니다.