Skip to content

Configuration

Advanced configuration options for Secure LSL.


Configuration File

Secure LSL uses LSL's standard configuration file (lsl_api.cfg) with an added [security] section.

File Locations

The configuration file is searched in this order:

  1. $LSLAPICFG environment variable (if set)
  2. ./lsl_api.cfg (current working directory)
  3. ~/.lsl_api/lsl_api.cfg (user home directory)
  4. /etc/lsl_api/lsl_api.cfg (system-wide, Linux/macOS)
  5. %PROGRAMDATA%\lsl_api\lsl_api.cfg (system-wide, Windows)

File Format

[security]
enabled = true
private_key = base64_encoded_ed25519_private_key_here
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400

[log]
level = 4

Security Section

enabled

Type: boolean Default: true (if private_key is present)

Enables or disables security. When disabled, the device operates in legacy insecure mode.

[security]
enabled = true

Warning

Setting enabled = false when other devices have security enabled will cause connection failures.

private_key

Type: string (base64-encoded) Required: Yes, unless encrypted_private_key is set

The Ed25519 private key for this device. Generated automatically by lsl-keygen.

[security]
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...

Never Share This

The private key uniquely identifies this device. Never copy it to other devices or share it.

encrypted_private_key

Type: string (base64-encoded) Required: No (alternative to private_key for two-factor authentication)

The Ed25519 private key encrypted with a passphrase. Generated by lsl-keygen --passphrase. This enables two-factor authentication:

  • Something you have: the key file
  • Something you know: the passphrase
[security]
encrypted_private_key = AAAAAAAAAAAAAAAA...  ; salt + nonce + encrypted key

NIS2 Compliance

Using passphrase-protected keys satisfies NIS2 multi-factor authentication requirements for environments where MFA is mandated.

Unlocking at runtime:

  1. Environment variable: Set LSL_KEY_PASSPHRASE before starting your application
  2. Programmatic: Call lsl_security_unlock(passphrase) in your code
# Option 1: Environment variable
export LSL_KEY_PASSPHRASE="your-passphrase"
./your_lsl_application
// Option 2: Programmatic unlock (C++)
#include <lsl_security.h>
auto& sec = lsl::security::LSLSecurity::instance();
sec.initialize();
if (sec.is_locked()) {
    sec.unlock("your-passphrase");
}

key_created

Type: ISO 8601 timestamp Default: Set by lsl-keygen

When the key was generated. Used for key rotation tracking.

[security]
key_created = 2025-12-05T19:00:00Z

session_key_lifetime

Type: integer (seconds) Default: 86400 (24 hours)

How often session keys are rotated. Lower values provide more forward secrecy but slightly more overhead.

[security]
session_key_lifetime = 86400  ; 24 hours
Value Use Case
3600 High-security clinical environments
86400 Standard research use (default)
604800 Long-running experiments with minimal overhead

public_key

Type: string (base64-encoded) Required: No (derived from private_key automatically)

The Ed25519 public key. Generated automatically from the private key.

[security]
public_key = PqyFnq8EdB4kkp88KBHZ2DuSy9qbEspO5QSUqPnUvc0=

Device-Bound Session Tokens

For passphrase-protected keys, you can create a device-bound session token to avoid entering the passphrase on every startup. The token is cryptographically bound to your specific hardware.

Creating a Session Token

# Create session token (default: 30 days expiry)
./lsl-config --remember-device --passphrase
# Enter your passphrase when prompted

# Create with custom expiry (90 days)
./lsl-config --remember-device --passphrase --days 90

# Create with no expiry (never expires)
./lsl-config --remember-device --passphrase --days -1

How It Works

  1. Device ID: A unique identifier derived from your hardware (CPU, motherboard, etc.)
  2. Token file: Stored in ~/.lsl_api/ with encrypted session key
  3. Auto-unlock: On startup, if token is valid and matches device, key unlocks automatically

Managing Session Tokens

# Show your device ID
./lsl-config --show-device-id

# Remove session token (will require passphrase again)
./lsl-config --forget-device

Security Properties

  • Device-bound: Token only works on the specific hardware where it was created
  • Time-limited: Expires after configured duration
  • Revocable: Can be removed with --forget-device
  • Hardware-tied: Moving the token file to another device won't work

Use Cases

  • Lab workstations: Create long-lived tokens for dedicated recording machines
  • Portable devices: Use shorter expiry for laptops that may be lost/stolen
  • Headless systems: Essential for servers that need unattended startup

Log Section

level

Type: integer (0-6) Default: 4 (Info)

Controls log verbosity:

Level Name Description
0 Off No logging
1 Fatal Only fatal errors
2 Error Errors
3 Warning Warnings and errors
4 Info General information
5 Verbose Detailed information
6 Debug Full debug output
[log]
level = 6  ; Enable debug logging for troubleshooting

Environment Variables

LSLAPICFG

Override the configuration file location:

export LSLAPICFG=/path/to/custom/lsl_api.cfg
./my_lsl_application

Useful for:

  • Testing with different configurations
  • Running multiple LSL instances with different keys
  • Containerized deployments

PYLSL_LIB

Point Python to a specific liblsl library:

export PYLSL_LIB=/path/to/liblsl.dylib
python my_script.py

Using lsl-keygen

The lsl-keygen tool generates keys and creates the configuration file.

Basic Usage

./lsl-keygen

By default, you will be prompted for a passphrase to protect the private key (like SSH keys). Creates keys at the default location (~/.lsl_api/lsl_api.cfg).

Options

Option Description
--output PATH Write configuration to specific path
--force Overwrite existing configuration
--insecure Store private key WITHOUT passphrase protection (not recommended)
--show-public Display public key and fingerprint after generation
--export NAME Generate and export keys to NAME.pub and NAME.key.enc files
--export-public Display current public key for sharing
--import FILE Import encrypted key file (.key.enc) into local config
--help Show help message

Examples

# Generate keys with passphrase protection (default)
./lsl-keygen

# Generate to specific file
./lsl-keygen --output /path/to/lsl_api.cfg

# Regenerate keys (overwrites existing)
./lsl-keygen --force

# Generate WITHOUT passphrase (not recommended for production)
./lsl-keygen --insecure

# Export keys for distribution to other devices
./lsl-keygen --export lab_eeg

# Import an exported key on another device
./lsl-keygen --import lab_eeg.key.enc

Passphrase Protection (Default)

By default, lsl-keygen prompts for a passphrase to encrypt the private key:

$ ./lsl-keygen
LSL Security Key Generator
==========================

Private key will be encrypted with a passphrase for security.
(Press Enter for no passphrase, but this is NOT recommended)

Enter passphrase: ********
Confirm passphrase: ********

Generating Ed25519 keypair...

[OK] Keypair generated successfully!
[OK] Configuration saved to: /Users/you/.lsl_api/lsl_api.cfg
[OK] Private key encrypted with passphrase (2FA enabled)

To unlock at runtime (in order of preference):
  1. Device-bound session token: lsl-config --remember-device (recommended)
  2. Environment variable: LSL_KEY_PASSPHRASE (less secure)
  3. Programmatic: call lsl_security_unlock() in your application

This satisfies NIS2 multi-factor authentication requirements by combining:

  • Something you have: the encrypted key file
  • Something you know: the passphrase

Environment Variable Security

While you can use LSL_KEY_PASSPHRASE for convenience, environment variables are visible to other processes on the same system. For better security, use device-bound session tokens instead:

./lsl-config --remember-device --passphrase

Low-Risk Environments

For closed lab environments without regulatory requirements (EU CRA, NIS2, HIPAA, GDPR), you can skip the passphrase:

  1. Press Enter twice when prompted for passphrase (requires confirmation)
  2. Use --insecure flag to skip the prompt entirely (shows warning)

Using lsl-config

The lsl-config tool validates and displays configuration.

Basic Usage

./lsl-config --check

Output

Security configuration status:
  Config file: /Users/you/.lsl_api/lsl_api.cfg
  Security enabled: true
  Key created: 2025-12-05T19:00:00Z
  Key fingerprint: SHA256:70:14:e1:b5:7f:93:ae:af...

[OK] Configuration valid

Options

Option Description
--check Validate configuration and show status (default)
--show-public Display public key and fingerprint for sharing
--show-device-id Display this device's unique hardware identifier
--remember-device Create device-bound session token (use with --passphrase)
--forget-device Remove device-bound session token
--days N Set session token expiry in days (use with --remember-device)
--check-network Scan network for LSL streams and verify security
--help Show help message

Displaying Public Key for Sharing

Use --show-public to display your device's public key:

$ ./lsl-config --show-public
LSL Device Public Key
=====================

Fingerprint:
  SHA256:70:14:e1:b5:7f:93:ae:af...

Public Key (base64):
  abc123DEF456...

This public key can be safely shared with other lab members.

Example Configurations

Research Lab (Default)

Standard configuration for most research environments:

[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400

[log]
level = 4

Clinical Environment

High-security configuration for clinical deployments:

[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 3600  ; Rotate keys hourly

[log]
level = 3  ; Warnings only in production

Development/Testing

Debug configuration for development:

[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400

[log]
level = 6  ; Full debug output

For backward compatibility with systems that cannot be updated:

[security]
enabled = false

[log]
level = 4

Security Risk

Legacy mode provides no encryption or authentication. Use only when absolutely necessary and only on isolated networks.


Multi-Device Setup

Shared Key Authorization Model

Secure LSL uses a shared keypair model for authorization. All devices that need to communicate securely must have the same keypair (public + private key). Devices with different keys will be rejected as "not authorized."

This model ensures:

  • Simple deployment: One key pair for your entire lab
  • Clear authorization: Only devices with your lab's key can connect
  • Easy management: Add new devices by importing the shared key

Setting Up Multiple Devices

Step 1: Generate the lab key on your admin/primary machine:

# Generate with passphrase protection (recommended)
./lsl-keygen --passphrase --force
# Enter a strong passphrase - you'll need this for all devices

# Export for distribution
./lsl-keygen --export lab_shared
# Creates: lab_shared.pub and lab_shared.key.enc

Step 2: Distribute to all lab devices:

# Copy encrypted key to each device
scp lab_shared.key.enc user@device1:~/.lsl_api/
scp lab_shared.key.enc user@device2:~/.lsl_api/
scp lab_shared.key.enc pi@raspberry-pi:/tmp/

Step 3: Import on each device:

# On each device, import the shared key
./lsl-keygen --import /path/to/lab_shared.key.enc
# Enter the same passphrase used during generation

# Verify the fingerprint matches
./lsl-config --show-public
# All devices should show: SHA256:xx:xx:xx:xx...

Step 4: (Optional) Create device-bound session tokens:

# On each device, create a session token for unattended operation
./lsl-config --remember-device --passphrase
# Enter passphrase once, then no more prompts on this device

Verification

All authorized devices should show the same fingerprint:

# On any device
./lsl-config --show-public

# Output should match across all devices:
# Fingerprint: SHA256:79:8c:1d:7d:a6:b4:34:22...

What Happens with Different Keys

If a device has a different key, connections are rejected:

Connection refused: 403 Public key mismatch - not authorized

This is intentional security behavior - only devices with your lab's shared key can participate in secure streams.

Security Considerations

  • Exported keys are always passphrase-protected (Argon2id + ChaCha20-Poly1305)
  • The .key.enc file is safe to transfer over untrusted networks
  • Each device needs the passphrase to unlock (or a device-bound session token)
  • Treat the passphrase like a lab password - share only with authorized personnel

Next Steps