Skip to content

Add opt-in printable TCP salt experiment for ss-local - #3063

Open
madeye wants to merge 1 commit into
masterfrom
feature/variable-printable-salt
Open

madeye wants to merge 1 commit into
masterfrom
feature/variable-printable-salt

Conversation

@madeye

@madeye madeye commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in, disabled-by-default client-side experiment: ss-local --printable-salt (or "printable_salt": true in the config). For each outgoing TCP salt it picks a prefix length uniformly from 6–12 bytes and overwrites those bytes with independently sampled printable ASCII (0x20–0x7e) right before key derivation.

  • Wire-compatible. The salt stays 32 bytes; no framing bytes, markers, or extra round trips. Any stock server (C or Rust) interoperates unchanged.
  • Scoped. Only chacha20-ietf-poly1305 and aes-256-gcm are accepted; other ciphers fail at startup, including when the cipher comes from a config file. UDP salts, server responses, and AEAD-2022 are untouched. The flag is ss-local only.
  • Entropy. Conditional salt entropy is (32-L)*8 + L*log2(95) ≥ 238.8 bits; salt uniqueness remains essential and is still enforced by the bloom filter.

This is motivated by the historical six-printable-byte exemption reported in the USENIX Security '23 GFW study. It is not TLS impersonation and not a claim of censorship resistance — see the caveats in docs/printable-salt.md. The 2026‑09‑23 field trials archived under docs/measurements/ both aborted before crossover; all recorded transfers (stock and modified) succeeded, so they show interoperability only, not an availability advantage.

Changes

  • src/aead.c, src/crypto.h: cipher_ctx.printable_salt flag; prefix rewrite on the first non-empty TCP encrypt, before aead_cipher_ctx_set_key.
  • src/local.c, src/jconf.[ch], src/common.h, src/utils.c: --printable-salt option, printable_salt config key, cipher validation, Doxygen CLI snippet, help text gated on MODULE_LOCAL.
  • completions/, doc/shadowsocks-c.md, README.md: flag documented.
  • tests/test_crypto.c: length distribution (all seven lengths over 512 samples), empty first write, exact framing overhead, salt-tail preservation, byte-at-a-time decrypt, tampering, replay rejection — for both ciphers, enabled and disabled.
  • tests/test_cli.py: local-only exposure; rejection via flag, config key, and non-boolean config value.
  • tests/interop.py: --printable-salt and --server-bin (independent stock server). New CTest test_printable_salt_interop.
  • tests/experiments/ + docs/encoding-experiments.md: Base85 / lowpop85 / verified-TLS outer-transport prototypes (research fixtures, not plugins), with codec and supervisor-cleanup tests registered in CTest.
  • tests/printable_salt_experiment.py, tests/check_printable_pcap.py: route-experiment runner and pcap first-payload checker.

Test plan

Verified locally on macOS arm64:

  • cmake --build build && ctest --test-dir build -LE memcheck — 39/39 passed (includes the 3 new integration tests)
  • python3 tests/stress_test.py --bin build/bin/ --size 10 — 3/3 passed
  • uvx ruff==0.15.6 check --select E9,F63,F7,F82 tests scripts — clean
  • python3 scripts/check_cli_docs.py and python3 -m unittest discover -s tests -p test_cli_docs.py — pass
  • Staged measurement data grepped for real IPs/hostnames — none
  • CI: asan, clang-tidy ratchet, Linux interop

🤖 Generated with Claude Code

Add `ss-local --printable-salt` / `"printable_salt": true`, disabled by
default. For each outgoing TCP salt it picks a prefix length uniformly
from 6-12 bytes and samples those bytes as printable ASCII (0x20-0x7e)
immediately before key derivation. The salt stays 32 bytes with no
framing or markers, so the wire format is unchanged and any stock
server interoperates. Only chacha20-ietf-poly1305 and aes-256-gcm are
accepted; other ciphers fail at startup. UDP, server responses, and
AEAD-2022 are untouched.

Crypto unit tests cover length distribution, empty first writes, exact
framing overhead, salt-tail preservation, byte-at-a-time decryption,
tampering and replay rejection. CLI tests check local-only exposure and
cipher rejection via flag, config key and bad config type. interop.py
gains --printable-salt and --server-bin for independent stock servers.

docs/printable-salt.md records the design, entropy bound, fingerprint
caveats and route-experiment procedure; docs/encoding-experiments.md
and tests/experiments/ hold the Base85/lowpop85/TLS outer-transport
prototypes. docs/measurements/printable-salt-2026-09-23/ archives the
sanitized results of the aborted field trials.

Co-Authored-By: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant