From d9aafde4319f79bbfbb83f8239203c907ec89954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wala?= <84684231+LVala@users.noreply.github.com> Date: Tue, 7 Nov 2023 08:43:41 +0100 Subject: [PATCH] Setup SRTP decryption (#12) --- .github/workflows/ci.yml | 2 ++ lib/ex_webrtc/dtls_transport.ex | 56 +++++++++++++++++++++++++++----- lib/ex_webrtc/peer_connection.ex | 17 +++++++--- mix.exs | 1 + mix.lock | 1 + 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc28e685..4ef22e08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,7 @@ jobs: with: otp-version: ${{matrix.otp}} elixir-version: ${{matrix.elixir}} + - run: sudo apt install -y libsrtp2-dev - run: mix deps.get - run: mix credo - run: mix dialyzer @@ -37,6 +38,7 @@ jobs: with: otp-version: ${{matrix.otp}} elixir-version: ${{matrix.elixir}} + - run: sudo apt install -y libsrtp2-dev - run: mix deps.get - run: mix coveralls.json - uses: codecov/codecov-action@v3 diff --git a/lib/ex_webrtc/dtls_transport.ex b/lib/ex_webrtc/dtls_transport.ex index 306f3ce6..39d1d2fa 100644 --- a/lib/ex_webrtc/dtls_transport.ex +++ b/lib/ex_webrtc/dtls_transport.ex @@ -14,6 +14,7 @@ defmodule ExWebRTC.DTLSTransport do :pkey, :fingerprint, :mode, + :srtp, finished: false ] @@ -25,11 +26,14 @@ defmodule ExWebRTC.DTLSTransport do {:ok, fingerprint} = ExDTLS.get_cert_fingerprint(cert_client) :ok = ExDTLS.stop(cert_client) + srtp = ExLibSRTP.new() + %__MODULE__{ ice_agent: ice_agent, cert: cert, pkey: pkey, - fingerprint: fingerprint + fingerprint: fingerprint, + srtp: srtp } end @@ -93,11 +97,11 @@ defmodule ExWebRTC.DTLSTransport do dtls end - def process_data(dtls, data) do + def process_data(dtls, <> = data) when f in 20..63 and not dtls.finished do case ExDTLS.process(dtls.client, data) do {:handshake_packets, packets} when dtls.ice_state in [:connected, :completed] -> :ok = ICEAgent.send_data(dtls.ice_agent, packets) - dtls + {:ok, dtls} {:handshake_packets, packets} -> Logger.debug(""" @@ -105,19 +109,53 @@ defmodule ExWebRTC.DTLSTransport do We will send those packets once ICE is ready. """) - %__MODULE__{dtls | buffered_packets: packets} + {:ok, %__MODULE__{dtls | buffered_packets: packets}} - {:handshake_finished, _keying_material, packets} -> + {:handshake_finished, keying_material, packets} -> Logger.debug("DTLS handshake finished") ICEAgent.send_data(dtls.ice_agent, packets) - %__MODULE__{dtls | finished: true} + # TODO: validate fingerprint + dtls = setup_srtp(dtls, keying_material) + {:ok, %__MODULE__{dtls | finished: true}} - {:handshake_finished, _keying_material} -> + {:handshake_finished, keying_material} -> Logger.debug("DTLS handshake finished") - %__MODULE__{dtls | finished: true} + dtls = setup_srtp(dtls, keying_material) + {:ok, %__MODULE__{dtls | finished: true}} :handshake_want_read -> - dtls + {:ok, dtls} + end + end + + def process_data(dtls, <> = data) when f in 128..191 and dtls.finished do + case ExLibSRTP.unprotect(dtls.srtp, data) do + {:ok, payload} -> + {:ok, dtls, payload} + + {:error, _reason} = err -> + err end end + + def process_data(_dtls, _data) do + {:error, :invalid_data} + end + + defp setup_srtp(dtls, keying_material) do + {_local_material, remote_material, profile} = keying_material + + {:ok, crypto_profile} = + ExLibSRTP.Policy.crypto_profile_from_dtls_srtp_protection_profile(profile) + + policy = %ExLibSRTP.Policy{ + ssrc: :any_inbound, + key: remote_material, + rtp: crypto_profile, + rtcp: crypto_profile + } + + :ok = ExLibSRTP.add_stream(dtls.srtp, policy) + dtls + end end diff --git a/lib/ex_webrtc/peer_connection.ex b/lib/ex_webrtc/peer_connection.ex index c9e1f69a..7af373fe 100644 --- a/lib/ex_webrtc/peer_connection.ex +++ b/lib/ex_webrtc/peer_connection.ex @@ -339,10 +339,19 @@ defmodule ExWebRTC.PeerConnection do end @impl true - def handle_info({:ex_ice, _from, {:data, data}}, state) - when not state.dtls_transport.finished do - dtls = DTLSTransport.process_data(state.dtls_transport, data) - {:noreply, %__MODULE__{state | dtls_transport: dtls}} + def handle_info({:ex_ice, _from, {:data, data}}, state) do + case DTLSTransport.process_data(state.dtls_transport, data) do + {:ok, dtls} -> + {:noreply, %__MODULE__{state | dtls_transport: dtls}} + + {:ok, dtls, payload} -> + notify(state.owner, {:data, payload}) + {:noreply, %__MODULE__{state | dtls_transport: dtls}} + + {:error, reason} -> + Logger.error("Unable to process data, reason: #{inspect(reason)}") + {:noreply, state} + end end @impl true diff --git a/mix.exs b/mix.exs index bcf27219..46d56344 100644 --- a/mix.exs +++ b/mix.exs @@ -48,6 +48,7 @@ defmodule ExWebRTC.MixProject do {:ex_sdp, "~> 0.13"}, {:ex_ice, "~> 0.1"}, {:ex_dtls, "~> 0.13"}, + {:ex_libsrtp, "~> 0.6"}, # dev/test {:excoveralls, "~> 0.14", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 913fadc4..3b274569 100644 --- a/mix.lock +++ b/mix.lock @@ -11,6 +11,7 @@ "ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"}, "ex_dtls": {:hex, :ex_dtls, "0.13.0", "4d7631eefc19a8820d4f79883f379ff2ad642976bda55493d4ec4e5d10d6c078", [:mix], [{:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "3ece30967006ec12a4088e60514cb08847814fba8b8a21aca3862e5d1fd4a6bc"}, "ex_ice": {:hex, :ex_ice, "0.1.0", "2653c884872d8769cf9fc655c74002a63ed6c21be1b3c2badfa42bdc74de2355", [:mix], [{:ex_stun, "~> 0.1.0", [hex: :ex_stun, repo: "hexpm", optional: false]}], "hexpm", "e2539a321f87f31997ba974d532d00511e5828f2f113b550b1ef6aa799dd2ffe"}, + "ex_libsrtp": {:hex, :ex_libsrtp, "0.6.0", "d96cd7fc1780157614f0bf47d31587e5eab953b43067f4885849f8177ec452a9", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e9ce8a507a658f7e2df72fae82a4b3ba0a056c175f0bc490e79ab03058e094d5"}, "ex_sdp": {:hex, :ex_sdp, "0.13.0", "b464cf5f6b70433159be243115857599f82b07234ee022997868c85ae1f225f7", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "975ca4d274240c51ee85909bc0618bd4dd940e69f7d8c8f0d701f1524eaffaad"}, "ex_stun": {:hex, :ex_stun, "0.1.0", "252474bf4c8519fbf4bc0fbfc6a1b846a634b1478c65dbbfb4b6ab4e33c2a95a", [:mix], [], "hexpm", "629fc8be45b624a92522f81d85ba001877b1f0745889a2419bdb678790d7480c"}, "excoveralls": {:hex, :excoveralls, "0.17.1", "83fa7906ef23aa7fc8ad7ee469c357a63b1b3d55dd701ff5b9ce1f72442b2874", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "95bc6fda953e84c60f14da4a198880336205464e75383ec0f570180567985ae0"},