feat: adopt smolmail protocol 1.1 with accept tokens and leave-mail-on-server
This commit is contained in:
parent
eef7bf9b1d
commit
d6fa097626
11 changed files with 911 additions and 232 deletions
38
test/e2e.py
38
test/e2e.py
|
|
@ -85,12 +85,18 @@ def main() -> int:
|
|||
run(bob + ["register", f"bob@{host}"])
|
||||
check("bob registers on the reference server", True)
|
||||
|
||||
# --- reference client sends to the JS client
|
||||
# bob needs alice as a known contact before he can accept her (§5.8).
|
||||
run(bob + ["resolve", f"alice@{host}"])
|
||||
|
||||
# --- reference client sends to the JS client, unsolicited: bob has not
|
||||
# accepted alice yet, so it lands in his requests tier, not the main one
|
||||
run(alice + ["send", f"bob@{host}", "--subject", "greetings", "--body", "hello bob"])
|
||||
print(run(bob + ["fetch"]).strip())
|
||||
bob_list = run(bob + ["list"])
|
||||
check("bob lists alice's message", "greetings" in bob_list, bob_list)
|
||||
bob_read = run(bob + ["read", re.match(r"([0-9a-f]{8})", bob_list).group(1)])
|
||||
bob_requests = run(bob + ["list", "--requests"])
|
||||
check("bob's requests tier holds alice's unsolicited message", "greetings" in bob_requests,
|
||||
bob_requests)
|
||||
check("bob's main tier is still empty", "greetings" not in run(bob + ["list"]))
|
||||
bob_read = run(bob + ["read", re.match(r"([0-9a-f]{8})", bob_requests).group(1)])
|
||||
alice_pub = re.search(r"public key: (\S+)", run(alice + ["whoami"])).group(1)
|
||||
check("bob reads the message", "hello bob" in bob_read, bob_read)
|
||||
check("bob sees alice's key", alice_pub in bob_read)
|
||||
|
|
@ -99,23 +105,34 @@ def main() -> int:
|
|||
check("reference client emits the signed reply address",
|
||||
"reply-to: smol://alice@" in bob_read, bob_read)
|
||||
|
||||
# --- JS client replies, through the bridge's WebSocket relay
|
||||
# --- bob accepts alice (§5.8): pushed to the server right away, so her
|
||||
# future mail reaches his main tier
|
||||
accepted = run(bob + ["accept", f"alice@{host}"])
|
||||
check("bob's server now holds an accept token", "server now holds 1 accept token" in accepted,
|
||||
accepted)
|
||||
|
||||
# --- JS client replies, through the bridge's WebSocket relay. Bob has
|
||||
# accepted alice, so this carries the token issued to her — but it is
|
||||
# still unsolicited from alice's side, landing in her requests tier.
|
||||
ws_env = dict(os.environ, GSMOL_TRANSPORT="ws", GSMOL_BRIDGE=f"ws://127.0.0.1:{bridge_port}")
|
||||
reply_id = re.search(r"sent ([0-9a-f]+)", run(bob + ["send", f"alice@{host}",
|
||||
"--subject", "re: greetings", "--body", "hello alice"], env=ws_env)).group(1)
|
||||
check("bob sends through the bridge", bool(reply_id))
|
||||
run(alice + ["fetch"])
|
||||
alice_list = run(alice + ["list"])
|
||||
check("alice lists bob's reply", "re: greetings" in alice_list, alice_list)
|
||||
alice_requests = run(alice + ["list", "--requests"])
|
||||
check("alice's requests tier holds bob's reply", "re: greetings" in alice_requests,
|
||||
alice_requests)
|
||||
alice_read = run(alice + ["read", reply_id[:8]])
|
||||
check("alice reads bob's reply", "hello alice" in alice_read, alice_read)
|
||||
check("reference client verifies the JS signature", "signature verified" in alice_read)
|
||||
# bob's reply carries his signed smol:// address; a receiver that does
|
||||
# not know the field must still preserve and display it (SPEC.md §5.5)
|
||||
check("reference client preserves the signed reply address",
|
||||
"reply-to:smol://bob@" in alice_read, alice_read)
|
||||
bool(re.search(r"reply-to:\s+smol://bob@", alice_read)), alice_read)
|
||||
|
||||
# --- rotation: the JS client rotates, the reference client walks the chain
|
||||
# --- having now learned bob's accept token from that reply's Accept
|
||||
# field, alice's next message to him carries a matching MAC and
|
||||
# reaches his main tier even after he rotates his key
|
||||
bob_rotated = run(bob + ["rotate"])
|
||||
new_key = re.search(r"new key: (\S+)", bob_rotated).group(1)
|
||||
resolved = run(alice + ["resolve", f"bob@{host}"])
|
||||
|
|
@ -124,7 +141,8 @@ def main() -> int:
|
|||
"--anonymous"])
|
||||
print(run(bob + ["fetch"]).strip())
|
||||
bob_list = run(bob + ["list"])
|
||||
check("bob reads mail addressed to the new key", "post-rotation" in bob_list)
|
||||
check("bob's main tier reaches mail addressed to the new key", "post-rotation" in bob_list,
|
||||
bob_list)
|
||||
# --anonymous omits the reply address; the field list must show none
|
||||
post_read = run(bob + ["read", re.search(r"([0-9a-f]{8}) .*post-rotation",
|
||||
bob_list).group(1)])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue