"""Demo server: python demo/server.py Accepts WebSocket clients on ws://127.0.0.1:8765, runs the handshake as responder (identifying each caller by trying its key store against msg1), prints the verification digest for comparison, then echoes messages back. Ctrl-C to stop. """ import asyncio from common import key_store, psk, save_store, setup_logging from websockets import serve from websockets.exceptions import ConnectionClosed from quantumpipe import ProtocolError, SecSocket, Verification, sas log = setup_logging("server") psk() # eager: create demo/psk before we listen (avoids a creation race) async def show_digest(v: Verification) -> None: # returning None answers with the app default: proceed unverified -- # the digest stays exposed (sock.session.verification) for an optional # comparison, confirmed later via sock.session.confirm; answering False # instead would delete the credential log.info("compare these emojis with the client:\n%s", sas.format(v.sas)) async def handle(ws) -> None: # per connection: re-read the store; the handshake mutates it in # place (rotation, flag, burn, delete) and we persist after store = key_store("server") sock = SecSocket(ws, store, handlers={Verification: show_digest}) try: await sock.accept() except (ProtocolError, ConnectionClosed) as e: log.info("handshake failed: %s", e) return finally: save_store("server", store) log.info( "handshake done (%s, verified=%s), echoing", sock.session.state, sock.session.verified, ) try: while True: msg = await sock.receive() log.info("received %r", msg) await sock.send(f"echo: {msg}") except ConnectionClosed: log.info("client disconnected") async def main() -> None: async with serve(handle, "127.0.0.1", 8765): log.info("listening on ws://127.0.0.1:8765") await asyncio.Future() # serve forever if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: pass