# jokycrypt

A small TCP relay that scrambles traffic with a deliberately *non*-cryptographic
stream cipher derived from a shared password. It is a teaching toy: fast,
transparent, and easy to read top to bottom. It is **not** safe for protecting
anything you actually care about — see the warning below before you get ideas.

## What it does

`jokycrypt` runs in front of a TCP service. Two instances form a tunnel:

```
your app  --plaintext-->  [jokycrypt client]  ==ciphertext==>  [jokycrypt server]  --plaintext-->  the service
```

The two relays perform a password handshake when they meet. If the password
matches on both sides, the connection is bridged and every byte between them is
XORed against a keystream seeded from that password plus a fresh per-connection
nonce. If the password does not match, nothing is forwarded — the connection is
dropped before either end is wired through.

The client end takes plaintext from your application and encrypts it onto the
wire; the server end decrypts it and hands plaintext to the real service. Replies
travel back the same way.

A server can front more than one service. Hand it a comma-separated allow-list of
targets and each client picks which one to reach with `--target`; leave the flag
off and the client lands on the first target the server lists. The choice is made
during the handshake and bound into the same proof that carries the password, so
nobody on the wire can quietly redirect a connection from one allowed target to
another. Asking for a target the server does not list gets you a clean refusal,
not a silent drop.

## Building

```sh
cargo build --release
# binary at target/release/jow
```

No third-party crates — only the Rust standard library, so the build is quick and
offline.

## Usage

```
jow <client|server> <LISTEN> <TARGET> [OPTIONS]

  client / server          The role comes first. 'server' decrypts toward the
                           target; 'client' encrypts toward the upstream relay.
  LISTEN                   Address to listen on, written as [host:]port. The host
                           is optional and defaults to all interfaces, so '8000'
                           listens on every IP.
  TARGET                   Where to forward to. A server takes a comma-separated
                           allow-list of targets, each written as [host:]port
                           (e.g. '80,443,192.168.1.50:8888'); a host defaults to
                           127.0.0.1. A client takes a single address: the server
                           to dial.

  -p, --password <PW>      Shared password, 1-64 bytes (also seeds the cipher)
                           Falls back to $JOKYCRYPT_PASSWORD when omitted
  -t, --target <TARGET>    (client) which of the server's allowed targets to
                           reach, as [host:]port; usually just a port. Defaults to
                           the server's first target.
  -c, --max-conns <N>      Cap on simultaneous connections [default: 2]
      --timeout <SECS>     Idle read/write timeout per socket [default: 60]
  -v, --verbose            Print the plaintext crossing the relay
  -h, --help               Show this help
```

Both addresses take an optional host. Drop it to keep the defaults — listening on
every interface and forwarding to localhost — or spell it out when you need a
specific IP or hostname (`192.168.1.254:9999`, `machine-b:9000`). IPv6 literals
go in brackets: `[::1]:8000`.

### A complete tunnel

Say you have a plain HTTP server on `localhost:7000` on machine *B* and you want
to reach it from machine *A* over an encrypted hop.

On *B*, decrypt incoming traffic and forward it to the real service:

```sh
jow server 9000 7000 --password hunter2
```

On *A*, accept plaintext from your browser and encrypt it toward *B*:

```sh
jow client 8000 machine-b:9000 --password hunter2
```

Now point your client at `localhost:8000` on *A*. The bytes flowing between *A*
and *B* are ciphertext; the password gates the whole thing.

Add `--verbose` on either end to watch the plaintext go by, `--max-conns` to cap
concurrency, and `--timeout` to change how long an idle socket is held (it
defaults to 60 seconds).

### Several targets behind one server

One server can guard a handful of services. List the ones it is willing to reach:

```sh
jow server 9000 80,443,192.168.1.50:8888 --password hunter2
```

A client with no `--target` lands on the first one (`127.0.0.1:80` here). To reach
another, name it — usually just by port:

```sh
jow client 8000 machine-b:9000 --target 443 --password hunter2
```

When two targets share a port on different hosts, spell out the host to pick
between them (`--target 192.168.1.50:8888`). Ask for anything not on the list and
the server refuses the connection rather than reaching somewhere it should not.

### Keeping the password off the command line

`--password` puts the secret in your shell history and in the process table,
where anyone running `ps` can read it. To avoid that, leave the flag off and set
`JOKYCRYPT_PASSWORD` in the environment instead:

```sh
export JOKYCRYPT_PASSWORD=hunter2
jow server 9000 7000
```

When both are present, `--password` wins. The same 1-64 byte rule applies either
way.
