Conversation
used brotli for compress data
Message latency was dominated by polling loops, not by cryptography. On a zero-latency channel a single message took 1100 ms end to end, while the entire crypto path (brotli + AES-GCM + ECDSA + WordCoder) accounts for 0.36 ms of that. Measurements were taken with two real CryptoLayer peers connected over an in-memory channel. Changes: - base: use queue.Queue instead of a list guarded by a lock and polled every 0.1 s. A waiting worker now wakes the moment data arrives rather than on the next tick, which removes eight level traversals worth of sleep from every message. The worker also runs outside the lock, so a transport send that blocks on acknowledgments no longer stalls the levels above it. - transport: wait for acknowledgments on a threading.Event instead of polling every 0.5 s. The retry path is now a bounded loop (6 attempts, 5 s apart, the same ~30 s budget as before) rather than a recursive call that grew the stack on every timeout and never gave up. - transport: send the chunks of one stream concurrently through a bounded pool. Waiting for each acknowledgment in turn cost a full round trip per chunk; the receiver already reassembles by chunk_id, so arrival order within a stream does not matter. - crypto_layer: wait for handshake data on events instead of polling, and read the send buffers with qsize() now that they are queues. Three reassembly bugs are fixed in the same change, because the higher throughput and the new retransmission path turn them from rare into routine: - WAITING_STREAMS was never cleared while stream_id wraps at 256, so the 257th stream appended its chunks to a completed one. Streams are now removed once delivered. - A retransmitted chunk was appended again, so chunk_count could be reached with a chunk still missing. Chunks are now keyed by chunk_id. - A retransmission arriving after its stream completed would rebuild and redeliver it. Recently completed packets are remembered in a bounded window, acknowledged again, and ignored. The wire format is unchanged: the same number of channel messages with the same byte layout, so existing peers stay compatible.
The project had no tests. These run two real CryptoLayer peers over an in-memory channel, so the whole stack is exercised without a network. - tests/test_pipeline.py asserts that what comes out is exactly what went in across payload sizes, chunk boundaries, unicode, multi-chunk streams, 30% packet loss and stream-id wraparound, and that the plaintext never reaches the channel. Plain asserts, no test framework needed. - bench/bench.py times each transform stage separately, which is how the polling overhead was identified as the real cost. - bench/e2e.py reports handshake time, per-message latency, throughput and channel cost, with configurable channel latency and payload entropy. - bench/loopback.py is the in-memory module the two above share. It can drop messages and record what reaches the channel. Requires the cryptolayer-module-interface dependency already declared in pyproject.toml.
The companion node id arrives from the network and is joined with the known_nodes directory path without any validation, so a peer (or the messenger relaying the session) could send "../../name" and make CryptoLayer probe and overwrite a file outside data_dir. The node id is sent before signature verification is enabled, so no authentication is needed to reach this. Accept a node id only if it has the expected shape - 64 lowercase hex characters, two uuid4 values - and verify that the resulting path stays inside known_nodes. The same check replaces the "length >= 64" test used when reading the local node id file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
perf: убрать опрос из конвейера, ускорив доставку сообщения с 1100 мс до 1 мс
docs: фикс отсутствия английского README.md но при этом его упоминание, а также добавление английской версии документации. (#24)
…-node-id fix: validate companion node id before using it as a file name
The application level processed every service packet regardless of the handshake state, so MY_PUBLIC_KEY was accepted while signature checking was still disabled (DO_SIGN is turned on only after the user confirms the companion signature). An active attacker in the channel could therefore replace the companion ECDH key and read one direction of the traffic, even when both users verified their fingerprints correctly. Add an explicit handshake state machine to the application level: service packets are accepted only at their own stage, MY_PUBLIC_KEY only after signature verification is enabled, and text/disconnect packets only after the handshake is complete. Data that reached the upper levels before verification was enabled is dropped, and the ECDH key is sent again when the companion key is accepted, so a key dropped by the peer does not stall the handshake. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rebased onto main. The Queue rework in #19 already wraps the worker calls, so a failure no longer kills a level thread - what is left is not sending packets that cannot be sent yet. Transitional.sworker signs every outgoing packet, but SIGN_PRIVATE_KEY is only set in signatures_setup, which runs after init_module. A messenger module can block in create_session for a long time (login, confirmation code), the transport ping timer fires after 30 seconds and pushes a packet down to a level that cannot sign it. Drop such packets explicitly instead of raising and logging a traceback every 30 seconds. The ping itself is now enabled by the core once init() is done, as requested in the review. Before that there is nothing to ping: the stack cannot send, and the handshake itself proves the companion is there. Without the flag, send_with_pending_ping could report a ping timeout to the UI during a slow start or a slow signature check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs: фикс того что я недоглядел (#24)
…chine fix: accept companion ECDH key only after signature verification is on
fix: keep level threads alive when a worker raises (#17)
- bind the ECDH shared secret to both public keys and a protocol label via HKDF-SHA256 instead of using the raw secret as the AES key - replace the 8-byte signature fingerprint with a 160-bit SHA-256 fingerprint computed over the whole key - enable companion signature trust only when the signature is actually accepted, so a user rejection no longer sets DO_SIGN Closes #27
…t / #major fix: аутентифицированный вывод AES-ключа и полноценный отпечаток подписи
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.