Documentation Index Fetch the complete documentation index at: https://mintlify.com/livekit/client-sdk-swift/llms.txt
Use this file to discover all available pages before exploring further.
The LiveKit Swift SDK supports end-to-end encryption (E2EE) for both media tracks and data messages, ensuring that only participants in the room can decrypt the content.
Overview
E2EE in LiveKit:
Encrypts media (audio/video) using frame encryption
Encrypts data messages using data packet encryption
Uses AES-GCM encryption algorithm
Supports both shared key and per-participant key modes
Keys are never sent to the LiveKit server
Setting Up E2EE
Create a BaseKeyProvider with a shared key:
let keyProvider = BaseKeyProvider (
isSharedKey : true ,
sharedKey : "your-secret-key-here"
)
For per-participant keys:
let keyProvider = BaseKeyProvider ( isSharedKey : false )
Create an E2EEManager with encryption options:
let encryptionOptions = EncryptionOptions (
keyProvider : keyProvider,
encryptionType : . gcm // AES-GCM encryption
)
let e2eeManager = E2EEManager ( options : encryptionOptions)
Attach the E2EE manager to the room before connecting:
let room = Room ()
room. e2eeManager = e2eeManager
e2eeManager. setup ( room : room)
try await room. connect ( url : wsURL, token : token)
Complete Example
import LiveKit
class E2EEExample {
let room = Room ()
func connectWithE2EE () async throws {
// Create key provider
let keyProvider = BaseKeyProvider (
isSharedKey : true ,
sharedKey : "my-secret-key"
)
// Create encryption options
let encryptionOptions = EncryptionOptions (
keyProvider : keyProvider,
encryptionType : . gcm
)
// Create and setup E2EE manager
let e2eeManager = E2EEManager ( options : encryptionOptions)
room. e2eeManager = e2eeManager
e2eeManager. setup ( room : room)
// Connect to room
try await room. connect (
url : "wss://your-livekit-server.com" ,
token : "your-token"
)
// Publish encrypted tracks
try await room. localParticipant . setCamera ( enabled : true )
try await room. localParticipant . setMicrophone ( enabled : true )
}
}
Key Management
Setting Keys
For shared key mode:
keyProvider. setKey (
key : "new-shared-key" ,
index : 0 // Key index in the key ring
)
For per-participant mode:
keyProvider. setKey (
key : "participant-key" ,
participantId : "participant-identity" ,
index : 0
)
Ratcheting Keys
Ratchet keys for forward secrecy:
// Shared key mode
let newKey = keyProvider. ratchetKey ( index : 0 )
// Per-participant mode
let newKey = keyProvider. ratchetKey (
participantId : "participant-identity" ,
index : 0
)
Exporting Keys
Export keys for backup or sharing:
// Shared key mode
if let keyData = keyProvider. exportKey ( index : 0 ) {
// Store or transmit keyData securely
}
// Per-participant mode
if let keyData = keyProvider. exportKey (
participantId : "participant-identity" ,
index : 0
) {
// Store or transmit keyData securely
}
Key Ring and Index
The key provider maintains a key ring (default size: 16):
// Get current key index
let currentIndex = keyProvider. getCurrentKeyIndex ()
// Set current key index
keyProvider. setCurrentKeyIndex ( 5 )
Advanced Configuration
Key Provider Options
Customize the key provider behavior:
let options = KeyProviderOptions (
sharedKey : true ,
ratchetSalt : "custom-salt" . data ( using : . utf8 ) ! ,
ratchetWindowSize : 0 , // Disable automatic ratcheting
uncryptedMagicBytes : "LK-ROCKS" . data ( using : . utf8 ) ! ,
failureTolerance : -1 ,
keyRingSize : 16
)
let keyProvider = BaseKeyProvider ( options : options)
Options:
sharedKey: Use shared key mode (true) or per-participant mode (false)
ratchetSalt: Salt for key ratcheting
ratchetWindowSize: Window size for automatic ratcheting (0 = disabled)
uncryptedMagicBytes: Magic bytes to identify unencrypted frames
failureTolerance: Number of decryption failures to tolerate
keyRingSize: Maximum number of keys in the ring
Enable/Disable Encryption
Toggle encryption at runtime:
// Disable encryption
e2eeManager. enableE2EE ( enabled : false )
// Re-enable encryption
e2eeManager. enableE2EE ( enabled : true )
Data Message Encryption
With E2EE enabled, data messages are automatically encrypted:
// Data will be encrypted if E2EE is enabled
let data = "Secret message" . data ( using : . utf8 ) !
try await room. localParticipant . publish ( data : data)
Check if data encryption is enabled:
if e2eeManager.isDataChannelEncryptionEnabled {
print ( "Data messages are encrypted" )
}
Monitoring Encryption State
Implement RoomDelegate to monitor encryption state changes:
class MyRoomDelegate : RoomDelegate {
func room (
_ room : Room,
trackPublication : TrackPublication,
didUpdateE2EEState state : E2EEState
) {
switch state {
case . new :
print ( "Encryption initialized" )
case . ok :
print ( "Encryption working" )
case . encryptionFailed :
print ( "Failed to encrypt" )
case . decryptionFailed :
print ( "Failed to decrypt" )
case . missingKey :
print ( "Missing encryption key" )
case . keyRatcheted :
print ( "Key was ratcheted" )
case . internalError :
print ( "Internal encryption error" )
@unknown default :
break
}
}
}
room. add ( delegate : MyRoomDelegate ())
Encryption Types
The SDK supports different encryption types:
public enum EncryptionType {
case none // No encryption
case gcm // AES-GCM (default)
case custom // Custom encryption
}
Access the current encryption type:
let frameEncryption = e2eeManager. frameEncryptionType
let dataEncryption = e2eeManager. dataChannelEncryptionType
Best Practices
Never transmit keys through the LiveKit server
Use a secure out-of-band channel for key distribution
Consider using a key management service
Rotate keys periodically for forward secrecy
Store keys securely using iOS Keychain or similar
Never hardcode keys in your application
Clear keys from memory when no longer needed
Monitor E2EE state changes via delegate
Handle missing key scenarios gracefully
Implement retry logic for transient failures
Log encryption errors for debugging
Troubleshooting
Decryption Failures
If participants can’t decrypt media:
Verify all participants use the same shared key
Check that E2EE manager is set up before connecting
Ensure key index matches across participants
Monitor E2EE state changes for specific errors
Missing Keys
If you see missingKey state:
func room (
_ room : Room,
trackPublication : TrackPublication,
didUpdateE2EEState state : E2EEState
) {
if state == .missingKey {
// Set the key for the participant
keyProvider. setKey (
key : "participant-key" ,
participantId : "participant-identity"
)
}
}
Legacy E2EEOptions
For backward compatibility, you can use E2EEOptions:
let e2eeOptions = E2EEOptions (
keyProvider : keyProvider,
encryptionType : . gcm
)
let e2eeManager = E2EEManager ( e2eeOptions : e2eeOptions)
See Also
E2EE/E2EEManager.swift
E2EE/KeyProvider.swift