# How Encryption Key Management Works in OpenFlux: Runtime Injection and scrypt Derivation

> Learn how OpenFlux manages encryption keys with runtime injection and scrypt derivation for secure AES-256-GCM keys. Discover its secure approach to handling secrets.

- Repository: [p1neappleXpress/OpenFlux](https://github.com/p1neappleXpress/OpenFlux)
- Tags: internals
- Published: 2026-09-13

---

**OpenFlux externalizes encryption keys through file-based runtime injection, deriving AES-256-GCM directional keys via scrypt KDF rather than embedding secrets in source code.**

OpenFlux is an open-source transport layer tool that secures communications between clients and exit nodes. Unlike systems that compile cryptographic material into binaries, OpenFlux implements strict separation between code and secrets, requiring operators to supply encryption keys at runtime through external files. This architecture ensures that the only secret traversing the network is ciphertext, while the clear-text key never leaves the host filesystem.

## Runtime Key Injection via Command-Line Flags

OpenFlux refuses to embed encryption keys in its source code. Instead, operators must provide the shared secret through a file path specified by the `--encryptionKeyFile` flag.

### Reading the Secret from Disk

Early in [`main/main.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/main/main.go) (lines 84–86), the program reads the raw bytes from the specified file, trims whitespace, and passes the result to the transport layer:

```go
secretBytes, err := os.ReadFile(*encryptionKeyFile)
...
encrypted, err := transport.NewEncryptedTransport(
    inner,
    strings.TrimSpace(string(secretBytes)),
    context,
    *exitNode,
)

```

The secret is treated as a **shared password** rather than a raw encryption key, triggering a rigorous key derivation process before any data encryption occurs.

## Cryptographic Key Derivation Architecture

Once injected, the shared secret undergoes a multi-stage transformation in [`transport/encrypted.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/transport/encrypted.go) to generate cryptographically independent directional keys.

### Master Key Generation with scrypt

The `NewEncryptedTransport` function treats the user-supplied secret as input to a scrypt-based **Key Derivation Function (KDF)**. The implementation (lines 58–62) uses the following parameters:
- **N (cost factor):** 32768
- **r (block size):** 8  
- **p (parallelization):** 1
- **Output length:** 32 bytes

The salt is deterministically generated via SHA-256:

```go
salt := sha256.Sum256([]byte("OpenFlux encrypted transport v1\x00" + context))
master, err := scrypt.Key([]byte(secret), salt[:], 32768, 8, 1, 32)

```

The **context** parameter—typically the transport type or document URL—ensures domain separation between different communication channels.

### Directional Key Separation

To prevent key reuse attacks, OpenFlux derives two independent keys from the master key using HMAC-SHA256 (lines 63–64):

```go
clientToExit := deriveDirectionalKey(master, "client-to-exit")
exitToClient := deriveDirectionalKey(master, "exit-to-client")

```

This **bidirectional isolation** ensures that compromising traffic in one direction does not expose the reverse channel.

## AES-256-GCM Transport Implementation

Each directional key initializes an AES-256-GCM **AEAD (Authenticated Encryption with Associated Data)** construct within the `EncryptedTransport` struct. This provides both confidentiality and integrity for every packet traversing the network.

### Replay Protection and Nonce Management

The transport layer maintains a sliding window that tracks the last 4096 nonces. Any packet containing a duplicate or stale nonce is automatically rejected, preventing replay attacks against the encrypted channel.

## Security Validation and Constraints

OpenFlux enforces strict safety checks before accepting encryption material. The `NewEncryptedTransport` function validates that secrets contain **at least 16 characters**, aborting initialization with an explicit error if shorter strings are supplied:

```go
if len(secret) < 16 {
    return nil, errors.New("encryption secret must contain at least 16 characters")
}

```

This minimum length requirement mitigates brute-force attacks against the scrypt-based KDF.

## Practical Implementation Examples

### Running OpenFlux with External Key Files

Create a strong secret and launch the client:

```bash

# Create a strong secret (at least 16 characters)

echo "my-super-secret-password-1234" > /path/to/flux.key

# Start the client with encryption enabled

./openflux \
    --encryptionKeyFile=/path/to/flux.key \
    --transport=websocket \
    --docUrl=https://example.com/mydoc \
    --socksAddr=:1080

```

### Programmatic Transport Creation

For testing or embedded Go applications:

```go
secret := []byte("a sufficiently long shared secret")
secretFile := "/tmp/flux.key"
os.WriteFile(secretFile, secret, 0600)

inner := transport.NewRawSocketTransport()
enc, err := transport.NewEncryptedTransport(
    inner,
    strings.TrimSpace(string(secret)),
    "example.com/doc",
    false,
)
if err != nil {
    log.Fatalf("cannot create encrypted transport: %v", err)
}

```

### Generating Cryptographically Secure Keys

Use system entropy for production deployments:

```bash
head -c 32 /dev/urandom | base64 > /secure/location/flux.key
chmod 600 /secure/location/flux.key

```

## Summary

- **Externalized secrets:** Encryption keys are read at runtime via `--encryptionKeyFile`, never embedded in the `p1neappleXpress/OpenFlux` source code.
- **Memory-hard KDF:** scrypt with parameters (32768, 8, 1) transforms short passwords into 32-byte master keys resistant to hardware-accelerated attacks.
- **Domain separation:** SHA-256 salts incorporate the transport context to ensure unique key derivation per communication channel.
- **Directional isolation:** HMAC-SHA256 derives independent client-to-exit and exit-to-client keys, preventing bidirectional cryptographic compromise.
- **AEAD enforcement:** AES-256-GCM provides authenticated encryption with a 4096-nonce replay protection window.
- **Length validation:** Mandatory 16-character minimum prevents weak shared secrets.

## Frequently Asked Questions

### Where does OpenFlux store its encryption keys?

OpenFlux does not store encryption keys internally. According to the source code in [`main/main.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/main/main.go), the binary expects an external file path via the `--encryptionKeyFile` flag. The operator maintains full custody of the key file, and OpenFlux only holds the derived cryptographic material in memory during runtime.

### What key derivation function does OpenFlux use?

OpenFlux uses **scrypt** as implemented in [`transport/encrypted.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/transport/encrypted.go) (lines 60–62). The function applies a memory-hard cost factor of 32768, block size of 8, and parallelization of 1 to derive a 32-byte master key. This KDF selection resists GPU and ASIC cracking attempts better than faster alternatives like PBKDF2.

### Why does OpenFlux use separate keys for each direction?

The transport layer derives independent directional keys via HMAC-SHA256 keyed with the master secret and distinct domain strings ("client-to-exit" versus "exit-to-client"). This architectural choice prevents reflection attacks and ensures that compromising one traffic direction does not automatically decrypt the reverse flow, limiting blast radius in case of key exposure.

### What happens if the encryption secret is shorter than 16 characters?

The `NewEncryptedTransport` constructor explicitly validates secret length and returns an error: `"encryption secret must contain at least 16 characters"`. The program aborts transport initialization rather than proceeding with weak cryptographic material, enforcing a baseline security standard for all OpenFlux deployments.