Blob Streams adds live, two-way encryption to CSha. Plaintext enters on the
sending device, encrypted byte chunks pass through the network or relay, and
the receiving device authenticates and decrypts them as they arrive. The same
connection carries replies in the opposite direction.Alice's device Relay/server Bob's device
private identity key no device private keys private identity key
trusted Bob certificate forwards opaque TLS bytes trusted Alice certificate
plaintext -> encryption ---------------------------------> decryption -> plaintext
plaintext <- decryption <--------------------------------- encryption <- plaintext
This is a C++ library and a macOS/Linux CLI, not a phone application or hosted
messaging service. Both users must be online. The included relay pairs one
conversation and exits afterward; it does not store messages for offline users.Identity and two-way keys#
Each device generates its own Ed25519 private key and self-signed public
certificate. The two users exchange only public certificates and verify their
SHA-256 public-key fingerprints over an already trusted channel (for example,
in person). The certificate is the explicit peer identity; a hostname or a
certificate supplied by the relay does not establish trust.The devices use OpenSSL TLS 1.3 with mutual certificate authentication,
ephemeral X25519 key agreement, and AES-256-GCM. TLS derives independent traffic
secrets for the two directions. Certificate verification is mandatory on both
sides, including the expected public key and certificate validity dates. The
private key never travels to the peer or relay. Generated certificates expire
after one year; generate and exchange a new identity to rotate it.Only TLS 1.3 and the csha-blob-stream/1 ALPN protocol are accepted. Early data,
session tickets and resumption are disabled. Every new connection performs a
fresh authenticated handshake. TLS authenticates records and their ordering;
replayed, modified, or out-of-order records fail. A clean stream ending requires
TLS close_notify; a raw TCP disconnect is reported as truncation. Previously
received complete records may already have been delivered before a later error.
Applications transferring an atomic file should write to a temporary destination
and publish it only after successful stream completion.These are separate keys and transport from the image-map formats. No map is
generated for network messages. The implementation uses the existing TLS
protocol rather than introducing a custom cryptographic handshake. References:
OpenSSL peer verification
and authenticated shutdown.Build#
OpenSSL 3.0 or newer with SSL and Crypto is required. The existing build produces
csha and csha_blob_streams; the latter links only OpenSSL, not OpenCV. Tests
also require Python 3.9+ and permission to open loopback sockets.cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failure
./build/csha blob-streams --help
Create device identities#
./build/csha blob-streams keygen --identity alice
./build/csha blob-streams fingerprint --peer alice.cert.pem
./build/csha blob-streams keygen --identity bob
./build/csha blob-streams fingerprint --peer bob.cert.pem
Exchange alice.cert.pem and bob.cert.pem and verify their fingerprints. Keep
alice.key.pem and bob.key.pem on their respective devices. The CLI creates
private keys with mode 0600 and refuses to overwrite an existing identity.
Keys are unencrypted PEM files; a phone integration should use device-protected
storage and supply the identity to the library locally. Never include private
keys in HTTP requests, logs, source control, or relay configuration.Direct communication#
Bob listens on an address reachable by Alice:./build/csha blob-streams listen --address 0.0.0.0:7443 \
--identity bob --peer alice.cert.pem
./build/csha blob-streams connect --address BOB_HOST:7443 \
--identity alice --peer bob.cert.pem
Both commands read local plaintext from stdin and write received plaintext to
stdout. Status and errors go to stderr, so stdout can be a binary file or pipe.
Terminal input is normally delivered after Enter; pipes are forwarded as bytes
become available. EOF closes only the local sending direction. Each process
continues receiving until the peer also sends an authenticated EOF.For example, Bob can receive a file while sending nothing:./build/csha blob-streams listen --address 0.0.0.0:7443 \
--identity bob --peer alice.cert.pem </dev/null >received.bin
./build/csha blob-streams connect --address BOB_HOST:7443 \
--identity alice --peer bob.cert.pem <source.bin >reply.bin
HTTP relay: both devices connect outward#
./build/csha blob-streams relay --address 0.0.0.0:8080 --room conversation-42
./build/csha blob-streams connect --relay RELAY_HOST:8080 \
--room conversation-42 --role initiator --identity alice --peer bob.cert.pem
./build/csha blob-streams connect --relay RELAY_HOST:8080 \
--room conversation-42 --role responder --identity bob --peer alice.cert.pem
The relay accepts the Blob Streams HTTP/1.1 CONNECT request for one configured
room and one endpoint of each role. It replies 200 Connection Established
once both endpoints have joined, then forwards the end-to-end TLS byte stream.
It never loads a device identity. Roles choose who starts the TLS handshake;
both endpoints can send and receive equally.This is a paired CONNECT relay, not a general-purpose HTTP proxy. Its exact
request is:Header lines use CRLF. The CLI accepts only its paired relay response. This is
not REST encryption or a browser HTTP client. The library below can be adapted
to an ordered WebSocket or other bidirectional byte transport.The HTTP room and routing information are visible to the relay and network.
Message sizes, timing, and endpoint addresses also remain visible. The room is
a pairing label, not an authentication key. An untrusted relay cannot impersonate
a pinned device, but it can withhold, interrupt, or misroute a connection. The
sample relay has no account system, rate limiting, persistence, or NAT traversal
beyond both devices connecting outward. Those belong in a deployed relay service.TLS and HTTP setup have a 10-second timeout. Start both relay clients within
that interval. --timeout SECONDS configures idle/accept timeouts (default 300,
range 1 through 86400). No automatic reconnect or plaintext retry occurs.Embed in a device or HTTP/WebSocket application#
The public header is src/streams/blob_stream.h. Link csha_blob_streams and
OpenSSL. The library itself uses no sockets and can be called by a mobile app's
network adapter; the provided CLI adapter uses POSIX sockets.Use one owner/thread per session. Keep transport bytes in order in each direction;
a session accepts arbitrary fragmentation, including individual bytes. Drain both
outputs after each receive/send operation, and apply transport backpressure.
Input and send calls accept at most 16 KiB; internal queues are capped at 256 KiB.
The CLI adds bounded queues and polls stdin, stdout, and the socket so a slow
consumer does not make memory grow with the conversation length. Error states
are terminal; discard a failed session and do not deliver queued plaintext from it.
finish() is idempotent, and sending after finishing is an error.The engine does not preserve application message boundaries: applications can
send newline-delimited text, length-prefixed messages, media frames, or binary
file bytes. It does not translate human languages.Validation and remaining integration#
Automated tests exercise mutual trust failures, mismatched private keys,
fragmented handshakes and binary records, live delivery before EOF, independent
directions, tampering, duplicate/out-of-order records, replay into a new session,
truncation, queue limits, and authenticated shutdown. Process tests exercise both
direct TCP and the HTTP relay, simultaneous transfers larger than all buffers,
private-key permissions and overwrite rejection.The tests run locally; no phone build, cellular-network test, public deployment,
or independent security audit has been performed.Command reference#
| Command | Required inputs | Result |
|---|
keygen | --identity PREFIX | New PREFIX.key.pem and PREFIX.cert.pem; refuses replacement |
fingerprint | --peer CERT.pem | Public-key fingerprint to compare through a trusted channel |
listen | --address HOST:PORT --identity PREFIX --peer CERT.pem | Accept one direct peer and exchange stdin/stdout bytes |
connect (direct) | --address HOST:PORT --identity PREFIX --peer CERT.pem | Connect to a listening peer |
relay | --address HOST:PORT --room NAME | Pair one initiator and one responder, then forward bytes |
connect (relay) | --relay HOST:PORT --room NAME --role initiator or responder, plus identity and peer | Join the paired relay and authenticate the end peer |
Use --timeout SECONDS for idle/accept timeout configuration. A relay endpoint
needs a matching room and opposite roles, but role selection does not grant
one side more authority or make it send-only.Local two-terminal walkthrough#
Generate both identities in a new test directory with the keygen commands
above. Since this is a local demonstration, both public certificates are
available there; on separate devices, exchange and verify them first.csha blob-streams listen --address 127.0.0.1:7443 \
--identity bob --peer alice.cert.pem
csha blob-streams connect --address 127.0.0.1:7443 \
--identity alice --peer bob.cert.pem
Type a line in either terminal and press Enter. It should appear in the other
terminal before either process exits. End stdin on both sides to finish the
session. A second conversation needs a new listening process.Publish a received file only on success#
For an atomic file workflow, receive into a temporary filename and rename it
only when the command exits successfully. Run in a new directory and choose
an unused final filename:csha blob-streams listen --address 127.0.0.1:7443 \
--identity bob --peer alice.cert.pem </dev/null >received.part && mv received.part received.bin
On failure, received.part may contain previously authenticated chunks but not
a complete file. Do not treat it as a successful transfer. This completion rule
also applies to adapters using the C++ session API. Modified at 2026-10-17 15:07:06