Architecture Overview
Technical details of Secure LSL's design and implementation.
System Architecture
Secure LSL integrates security at the core library level, making it transparent to all applications:
flowchart TB
subgraph Apps["Your Applications (unchanged)"]
A1[Python Script]
A2[MATLAB Code]
A3[C++ Application]
A4[LabRecorder]
end
subgraph LibLSL["liblsl with Security Layer"]
direction TB
S1[Security Module]
S2[Encryption/Decryption]
S3[Key Management]
S4[Session Handling]
subgraph Original["Original LSL"]
L1[Stream Discovery]
L2[Data Serialization]
L3[Network Transport]
L4[Time Synchronization]
end
S1 --> S2
S1 --> S3
S1 --> S4
S2 --> L3
end
subgraph Crypto["libsodium"]
C1[Ed25519]
C2[X25519]
C3[ChaCha20-Poly1305]
C4[HKDF]
end
A1 --> LibLSL
A2 --> LibLSL
A3 --> LibLSL
A4 --> LibLSL
S1 --> Crypto
style Apps fill:#E8F5E9
style S1 fill:#BBDEFB
style S2 fill:#BBDEFB
style S3 fill:#BBDEFB
style S4 fill:#BBDEFB
Security Integration Points
Security is implemented at specific points in the LSL protocol:
1. Stream Discovery (UDP)
sequenceDiagram
participant Outlet as Outlet
participant Network as Network (Multicast)
participant Inlet as Inlet
Note over Outlet,Inlet: Discovery Request
Inlet->>Network: "Looking for EEG streams"
Network->>Outlet: Query received
Note over Outlet,Inlet: Discovery Response (Extended)
Outlet->>Network: Stream info + Security metadata
Note right of Outlet: Includes:<br/>- security_enabled: true<br/>- public_key: [32 bytes]<br/>- fingerprint: SHA256:...
Network->>Inlet: Response received
Inlet->>Inlet: Validate security metadata
The discovery response XML now includes a <security> node:
<info>
<name>MyEEG</name>
<type>EEG</type>
<channel_count>64</channel_count>
...
<security>
<enabled>true</enabled>
<public_key>base64_encoded_key</public_key>
<fingerprint>SHA256:70:14:e1:b5:...</fingerprint>
</security>
</info>
2. Connection Establishment (TCP)
sequenceDiagram
participant Inlet as Inlet
participant Outlet as Outlet
Note over Inlet,Outlet: TCP Connection
Inlet->>Outlet: Connect
Note over Inlet,Outlet: Security Negotiation
Inlet->>Outlet: LSL/1.16 GET /stream<br/>Security-Enabled: true<br/>Security-Public-Key: [base64]
alt Security Match (both enabled)
Outlet->>Inlet: LSL/1.16 200 OK<br/>Security-Enabled: true<br/>Security-Public-Key: [base64]
Note over Inlet,Outlet: Key Exchange (X25519)
Inlet->>Inlet: Derive session key
Outlet->>Outlet: Derive session key
Note over Inlet,Outlet: Encrypted Data Streaming
Outlet->>Inlet: [Encrypted samples]
else Security Mismatch
Outlet->>Inlet: LSL/1.16 403 Security required<br/>but client has no security enabled
Note over Inlet: Connection rejected
end
3. Data Transmission
Each data chunk is encrypted independently:
flowchart LR
subgraph Original["Original Data Chunk"]
direction LR
M1[Metadata<br/>timestamps, etc.] --> P1[Payload<br/>sample data]
end
subgraph Encrypted["Encrypted Chunk"]
direction LR
M2[Metadata<br/>plaintext] --> N[Nonce<br/>8 bytes] --> E[Encrypted Payload] --> T[Auth Tag<br/>16 bytes]
end
Original -->|"ChaCha20-Poly1305"| Encrypted
Wire format:
| Field | Size | Description |
|---|---|---|
| Length | 4 bytes | Total chunk length |
| Nonce | 8 bytes | Unique per packet, monotonic |
| Ciphertext | variable | Encrypted sample data |
| Auth Tag | 16 bytes | Poly1305 authentication |
Key Management
Key Hierarchy
flowchart TD
subgraph Device["Per-Device (Permanent)"]
PK[Ed25519 Private Key<br/>Stored in lsl_api.cfg]
PUB[Ed25519 Public Key<br/>Derived from private]
end
subgraph Connection["Per-Connection (Ephemeral)"]
SS[Shared Secret<br/>X25519 key agreement]
SK[Session Key<br/>HKDF-derived]
end
subgraph Packet["Per-Packet"]
N[Nonce<br/>Monotonic counter]
end
PK --> PUB
PK --> SS
SS --> SK
SK --> N
style PK fill:#FFE4B5
style SS fill:#90EE90
style SK fill:#90EE90
style N fill:#ADD8E6
Session Key Derivation
flowchart LR
subgraph Inlet
IA[Inlet Private Key]
IB[Inlet Public Key]
end
subgraph Outlet
OA[Outlet Private Key]
OB[Outlet Public Key]
end
subgraph KeyExchange["X25519 Key Agreement"]
SS[Shared Secret<br/>Both sides compute same value]
end
subgraph Derivation["HKDF"]
SK[Session Key<br/>32 bytes]
end
IA --> SS
OB --> SS
OA --> SS
IB --> SS
SS --> SK
Both sides independently compute the same shared secret:
- Inlet computes: X25519(inlet_private, outlet_public)
- Outlet computes: X25519(outlet_private, inlet_public)
Mathematical properties of X25519 ensure these produce identical results.
Session Key Rotation
Keys are rotated every 24 hours using a make-before-break pattern:
sequenceDiagram
participant Outlet
participant Inlet
Note over Outlet,Inlet: Normal Operation with Key A
loop Every packet
Outlet->>Inlet: Encrypt with Key A
end
Note over Outlet,Inlet: Rotation Trigger (24 hours)
Outlet->>Outlet: Generate new key exchange material
Outlet->>Inlet: Key rotation notification
Note over Outlet,Inlet: Grace Period (60 seconds)
Outlet->>Inlet: Encrypt with Key B
Note right of Inlet: Accept Key A or Key B
Note over Outlet,Inlet: Rotation Complete
Outlet->>Outlet: Discard Key A
Inlet->>Inlet: Discard Key A
loop Every packet
Outlet->>Inlet: Encrypt with Key B only
end
Modified Source Files
Security is implemented by modifying only a few core liblsl files:
| File | Changes |
|---|---|
src/lsl_security.cpp |
New: All cryptographic operations |
src/api_config.cpp |
Extended: Parse [security] config section |
src/stream_info_impl.cpp |
Extended: Security metadata in stream info |
src/tcp_server.cpp |
Extended: Security handshake, encryption |
src/data_receiver.cpp |
Extended: Security handshake, decryption |
include/lsl/streaminfo.h |
Extended: Security status API |
include/lsl_cpp.h |
Extended: C++ security methods |
Total: ~800 lines of security code added to liblsl.
Configuration
Security settings are stored in LSL's standard configuration file:
[security]
enabled = true
private_key = base64_encoded_ed25519_private_key
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400 ; 24 hours in seconds
[log]
level = 6 ; Enable security event logging
Configuration file search order:
$LSLAPICFGenvironment variable (if set)./lsl_api.cfg(current directory)~/.lsl_api/lsl_api.cfg(user home)/etc/lsl_api/lsl_api.cfg(system-wide)
Dependencies
libsodium
Secure LSL uses libsodium for all cryptographic operations:
- Version: 1.0.18 or later
- Algorithms used:
crypto_sign_ed25519- Device identity signaturescrypto_scalarmult_curve25519- Key agreementcrypto_aead_chacha20poly1305_ietf- Authenticated encryptioncrypto_kdf_hkdf_sha256- Key derivationsodium_memzero- Secure memory clearing
libsodium is: - Extensively audited - Constant-time implementations (side-channel resistant) - Available on all major platforms - Used by Signal, Wireguard, and many security-critical projects
Next Steps
- Protocol Flow - Detailed message sequences
- Cryptographic Design - Algorithm choices and rationale
- API Reference - Using the security API