Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion livekit-rtc/livekit/rtc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"""

from ._proto import stats_pb2 as stats
from ._proto.e2ee_pb2 import EncryptionState, EncryptionType
from ._proto.e2ee_pb2 import EncryptionState, EncryptionType, KeyDerivationFunction
from ._proto.participant_pb2 import ParticipantKind, ParticipantState, DisconnectReason
from ._proto.room_pb2 import (
ConnectionQuality,
Expand Down Expand Up @@ -129,6 +129,7 @@
"IceServer",
"EncryptionType",
"EncryptionState",
"KeyDerivationFunction",
"StreamState",
"TrackKind",
"TrackSource",
Expand Down
5 changes: 5 additions & 0 deletions livekit-rtc/livekit/rtc/e2ee.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DEFAULT_RATCHET_SALT = b"LKFrameEncryptionKey"
DEFAULT_RATCHET_WINDOW_SIZE = 16
DEFAULT_FAILURE_TOLERANCE = -1
DEFAULT_KEY_RING_SIZE = 16


@dataclass
Expand All @@ -30,6 +31,10 @@ class KeyProviderOptions:
ratchet_salt: bytes = DEFAULT_RATCHET_SALT
ratchet_window_size: int = DEFAULT_RATCHET_WINDOW_SIZE
failure_tolerance: int = DEFAULT_FAILURE_TOLERANCE
key_ring_size: int = DEFAULT_KEY_RING_SIZE
key_derivation_function: proto_e2ee.KeyDerivationFunction.ValueType = (
proto_e2ee.KeyDerivationFunction.PBKDF2
)


@dataclass
Expand Down
12 changes: 12 additions & 0 deletions livekit-rtc/livekit/rtc/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ def on_participant_connected(participant):
req.connect.options.e2ee.key_provider_options.ratchet_window_size = (
options.e2ee.key_provider_options.ratchet_window_size
)
req.connect.options.e2ee.key_provider_options.key_ring_size = (
options.e2ee.key_provider_options.key_ring_size
)
req.connect.options.e2ee.key_provider_options.key_derivation_function = (
options.e2ee.key_provider_options.key_derivation_function
)

if options.encryption:
req.connect.options.encryption.encryption_type = options.encryption.encryption_type
Expand All @@ -487,6 +493,12 @@ def on_participant_connected(participant):
req.connect.options.encryption.key_provider_options.ratchet_window_size = (
options.encryption.key_provider_options.ratchet_window_size
)
req.connect.options.encryption.key_provider_options.key_ring_size = (
options.encryption.key_provider_options.key_ring_size
)
req.connect.options.encryption.key_provider_options.key_derivation_function = (
options.encryption.key_provider_options.key_derivation_function
)

if options.rtc_config:
req.connect.options.rtc_config.ice_transport_type = (
Expand Down
13 changes: 13 additions & 0 deletions livekit-rtc/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
204 changes: 204 additions & 0 deletions livekit-rtc/tests/test_e2ee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Unit tests for E2EE functionality."""

import pytest

Check failure on line 17 in livekit-rtc/tests/test_e2ee.py

View workflow job for this annotation

GitHub Actions / build

ruff (F401)

livekit-rtc/tests/test_e2ee.py:17:8: F401 `pytest` imported but unused help: Remove unused import: `pytest`


class TestKeyProviderOptions:
"""Tests for KeyProviderOptions dataclass."""

def test_default_values(self):
"""Test that KeyProviderOptions has correct default values."""
from livekit.rtc.e2ee import (
KeyProviderOptions,
DEFAULT_RATCHET_SALT,
DEFAULT_RATCHET_WINDOW_SIZE,
DEFAULT_FAILURE_TOLERANCE,
DEFAULT_KEY_RING_SIZE,
)
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

options = KeyProviderOptions()

assert options.shared_key is None
assert options.ratchet_salt == DEFAULT_RATCHET_SALT
assert options.ratchet_window_size == DEFAULT_RATCHET_WINDOW_SIZE
assert options.failure_tolerance == DEFAULT_FAILURE_TOLERANCE
assert options.key_ring_size == DEFAULT_KEY_RING_SIZE
assert options.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2

def test_custom_values(self):
"""Test KeyProviderOptions with custom values."""
from livekit.rtc.e2ee import KeyProviderOptions
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

options = KeyProviderOptions(
shared_key=b"my-secret-key",
ratchet_salt=b"custom-salt",
ratchet_window_size=32,
failure_tolerance=5,
key_ring_size=8,
key_derivation_function=proto_e2ee.KeyDerivationFunction.HKDF,
)

assert options.shared_key == b"my-secret-key"
assert options.ratchet_salt == b"custom-salt"
assert options.ratchet_window_size == 32
assert options.failure_tolerance == 5
assert options.key_ring_size == 8
assert options.key_derivation_function == proto_e2ee.KeyDerivationFunction.HKDF

def test_various_key_lengths(self):
"""Test that shared_key accepts various lengths."""
from livekit.rtc.e2ee import KeyProviderOptions

# Short key
options_short = KeyProviderOptions(shared_key=b"short")
assert options_short.shared_key == b"short"

# Medium key
options_medium = KeyProviderOptions(shared_key=b"medium-length-key-here")
assert options_medium.shared_key == b"medium-length-key-here"

# Long key
long_key = b"a" * 256
options_long = KeyProviderOptions(shared_key=long_key)
assert options_long.shared_key == long_key

# Binary key
binary_key = bytes(range(256))
options_binary = KeyProviderOptions(shared_key=binary_key)
assert options_binary.shared_key == binary_key


class TestE2EEOptions:
"""Tests for E2EEOptions dataclass."""

def test_default_values(self):
"""Test E2EEOptions default values."""
from livekit.rtc.e2ee import E2EEOptions, KeyProviderOptions
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

options = E2EEOptions()

assert isinstance(options.key_provider_options, KeyProviderOptions)
assert options.encryption_type == proto_e2ee.EncryptionType.GCM

def test_with_shared_key(self):
"""Test E2EEOptions with a shared key."""
from livekit.rtc.e2ee import E2EEOptions, KeyProviderOptions

key_options = KeyProviderOptions(shared_key=b"test-key")
options = E2EEOptions(key_provider_options=key_options)

assert options.key_provider_options.shared_key == b"test-key"


class TestProtoMessageBuilding:
"""Tests for proto message building with E2EE options."""

def test_proto_key_provider_options_fields(self):
"""Test that proto KeyProviderOptions has all required fields."""
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

proto_options = proto_e2ee.KeyProviderOptions()

# Set all fields that should be present
proto_options.shared_key = b"test-key"
proto_options.ratchet_window_size = 16
proto_options.ratchet_salt = b"LKFrameEncryptionKey"
proto_options.failure_tolerance = -1
proto_options.key_ring_size = 16
proto_options.key_derivation_function = proto_e2ee.KeyDerivationFunction.PBKDF2

# Verify fields are set correctly
assert proto_options.shared_key == b"test-key"
assert proto_options.ratchet_window_size == 16
assert proto_options.ratchet_salt == b"LKFrameEncryptionKey"
assert proto_options.failure_tolerance == -1
assert proto_options.key_ring_size == 16
assert proto_options.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2

def test_proto_serialization(self):
"""Test that proto message can be serialized without errors."""
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

proto_options = proto_e2ee.KeyProviderOptions()
proto_options.ratchet_window_size = 16
proto_options.ratchet_salt = b"LKFrameEncryptionKey"
proto_options.failure_tolerance = -1
proto_options.key_ring_size = 16
proto_options.key_derivation_function = proto_e2ee.KeyDerivationFunction.PBKDF2

# This should not raise an EncodeError
serialized = proto_options.SerializeToString()
assert len(serialized) > 0

# Verify we can deserialize it back
parsed = proto_e2ee.KeyProviderOptions()
parsed.ParseFromString(serialized)
assert parsed.key_ring_size == 16
assert parsed.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2

def test_e2ee_options_proto_serialization(self):
"""Test full E2eeOptions proto serialization."""
from livekit.rtc._proto import e2ee_pb2 as proto_e2ee

e2ee_opts = proto_e2ee.E2eeOptions()
e2ee_opts.encryption_type = proto_e2ee.EncryptionType.GCM
e2ee_opts.key_provider_options.shared_key = b"my-shared-key"
e2ee_opts.key_provider_options.ratchet_window_size = 16
e2ee_opts.key_provider_options.ratchet_salt = b"LKFrameEncryptionKey"
e2ee_opts.key_provider_options.failure_tolerance = -1
e2ee_opts.key_provider_options.key_ring_size = 16
e2ee_opts.key_provider_options.key_derivation_function = (
proto_e2ee.KeyDerivationFunction.PBKDF2
)

# This should not raise an EncodeError
serialized = e2ee_opts.SerializeToString()
assert len(serialized) > 0


class TestPublicExports:
"""Tests for public API exports."""

def test_key_derivation_function_exported(self):
"""Test that KeyDerivationFunction is exported from the package."""
from livekit.rtc import KeyDerivationFunction

# Verify enum values are accessible
assert KeyDerivationFunction.PBKDF2 == 0
assert KeyDerivationFunction.HKDF == 1

def test_encryption_type_exported(self):
"""Test that EncryptionType is exported from the package."""
from livekit.rtc import EncryptionType

assert EncryptionType.NONE == 0
assert EncryptionType.GCM == 1
assert EncryptionType.CUSTOM == 2

def test_e2ee_classes_exported(self):
"""Test that E2EE classes are exported from the package."""
from livekit.rtc import E2EEOptions, KeyProviderOptions

# Should be able to instantiate without errors
key_opts = KeyProviderOptions()
e2ee_opts = E2EEOptions()

assert key_opts is not None
assert e2ee_opts is not None
Loading