Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify peer certificate fingerprint #19

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 26 additions & 15 deletions lib/ex_webrtc/dtls_transport.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ defmodule ExWebRTC.DTLSTransport do
end

@doc false
@spec start_dtls(dtls_transport(), :active | :passive) :: :ok | {:error, :already_started}
def start_dtls(dtls_transport, mode) do
@spec start_dtls(dtls_transport(), :active | :passive, binary()) ::
:ok | {:error, :already_started}
def start_dtls(dtls_transport, mode, peer_fingerprint) do
GenServer.call(dtls_transport, {:start_dtls, mode})
end

Expand All @@ -43,11 +44,8 @@ defmodule ExWebRTC.DTLSTransport do

@impl true
def init([ice_config, ice_module, owner]) do
# temporary hack to generate certs
dtls = ExDTLS.init(client_mode: true, dtls_srtp: true)
cert = ExDTLS.get_cert(dtls)
pkey = ExDTLS.get_pkey(dtls)
fingerprint = ExDTLS.get_cert_fingerprint(dtls)
{pkey, cert} = ExDTLS.generate_key_cert()
fingerprint = ExDTLS.get_cert_fingerprint()

{:ok, ice_agent} = ice_module.start_link(:controlled, ice_config)
srtp = ExLibSRTP.new()
Expand All @@ -60,6 +58,7 @@ defmodule ExWebRTC.DTLSTransport do
cert: cert,
pkey: pkey,
fingerprint: fingerprint,
peer_fingerprint: nil,
srtp: srtp,
dtls_state: :new,
dtls: nil,
Expand All @@ -80,17 +79,18 @@ defmodule ExWebRTC.DTLSTransport do
end

@impl true
def handle_call({:start_dtls, mode}, _from, %{dtls: nil} = state)
when mode in [:active, :passive] do
def handle_call({:start_dtls, mode, peer_fingerprint}, _from, %{dtls: nil} = state)
when mode in [:active, :passive] and is_binary(peer_fingerprint) do
dtls =
ExDTLS.init(
client_mode: mode == :active,
dtls_srtp: true,
pkey: state.pkey,
cert: state.cert
cert: state.cert,
verify_peer: true
)

state = %{state | dtls: dtls, mode: mode}
state = %{state | dtls: dtls, mode: mode, peer_fingerprint: peer_fingerprint}
{:reply, :ok, state}
end

Expand Down Expand Up @@ -177,9 +177,18 @@ defmodule ExWebRTC.DTLSTransport do
{:handshake_finished, _, remote_keying_material, profile, packets} ->
Logger.debug("DTLS handshake finished")
ICEAgent.send_data(state.ice_agent, packets)
# TODO: validate fingerprint
state = setup_srtp(state, remote_keying_material, profile)
%{state | dtls_state: :connected}

peer_fingerprint =
state.dtls
|> ExDTLS.get_peer_cert()
|> ExDTLS.get_cert_fingerprint()

if peer_fingerprint == state.peer_fingerprint do
state = setup_srtp(state, remote_keying_material, profile)
%{state | dtls_state: :connected}
else
%{state | dtls_state: :failed}
end

{:handshake_finished, _, remote_keying_material, profile} ->
Logger.debug("DTLS handshake finished")
Expand All @@ -196,7 +205,7 @@ defmodule ExWebRTC.DTLSTransport do
case ExLibSRTP.unprotect(state.srtp, data) do
{:ok, payload} ->
# TODO: temporarily, everything goes to peer connection process
send(state.owner, {:rtp_data, payload})
notify(state.owner, {:rtp_data, payload})

{:error, reason} ->
Logger.warning("Failed to decrypt SRTP, reason: #{inspect(reason)}")
Expand Down Expand Up @@ -260,4 +269,6 @@ defmodule ExWebRTC.DTLSTransport do
:ok = ExLibSRTP.add_stream(state.srtp, policy)
state
end

defp notify(dst, msg), do: send(dst, {:dtls_transport, self(), msg})
end
61 changes: 57 additions & 4 deletions lib/ex_webrtc/peer_connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ defmodule ExWebRTC.PeerConnection do
streams: [:TODO]
]

@typedoc """
Messages sent by the ExWebRTC.
"""
@type signal() :: {:ex_webrtc, pid(), connection_state_change()}

@type connection_state_change() :: {:connectionstatechange, connection_state()}

@typedoc """
Possible PeerConnection states.

For the exact meaning, refer to the [WebRTC W3C, section 4.3.3](https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum)
"""
@type connection_state() :: :closed | :failed | :disconnected | :new | :connecting | :connected

@enforce_keys [:config, :owner]
defstruct @enforce_keys ++
[
Expand All @@ -39,11 +53,13 @@ defmodule ExWebRTC.PeerConnection do
:current_remote_desc,
:pending_remote_desc,
:ice_agent,
:ice_state,
:dtls_transport,
demuxer: %Demuxer{},
transceivers: [],
ice_state: nil,
dtls_state: nil,
signaling_state: :stable,
conn_state: :new,
last_offer: nil,
last_answer: nil
]
Expand Down Expand Up @@ -122,6 +138,8 @@ defmodule ExWebRTC.PeerConnection do
dtls_transport: dtls_transport
}

notify(state.owner, {:connectionstatechange, :new})

{:ok, state}
end

Expand Down Expand Up @@ -327,8 +345,11 @@ defmodule ExWebRTC.PeerConnection do
end

@impl true
def handle_info({:ex_ice, _from, :connected}, state) do
{:noreply, %__MODULE__{state | ice_state: :connected}}
def handle_info({:ex_ice, _from, msg}, state)
when msg in [:checking, :connected, :completed, :failed] do
next_conn_state = next_conn_state(msg, state.dtls_state)
state = update_conn_state(state, next_conn_state)
{:noreply, %__MODULE__{state | ice_state: msg}}
end

@impl true
Expand All @@ -346,7 +367,14 @@ defmodule ExWebRTC.PeerConnection do
end

@impl true
def handle_info({:rtp_data, data}, state) do
def handle_info({:dtls_transport, _pid, {:statechange, new_dtls_state}}, state) do
next_conn_state = next_conn_state(state.ice_state, new_dtls_state)
state = update_conn_state(state, next_conn_state)
{:noreply, state}
end

@impl true
def handle_info({:dtls_transport, _pid, {:rtp_data, data}}, state) do
case Demuxer.demux(state.demuxer, data) do
{:ok, demuxer, mid, packet} ->
notify(state.owner, {:data, {mid, packet}})
Expand Down Expand Up @@ -514,6 +542,31 @@ defmodule ExWebRTC.PeerConnection do
defp maybe_next_state(:have_remote_pranswer, :remote, :answer), do: {:ok, :stable}
defp maybe_next_state(:have_remote_pranswer, _, _), do: {:error, :invalid_transition}

# TODO support :disconnected state - our ICE doesn't provide disconnected state for now
# TODO support :closed state
defp next_conn_state(ice_state, dtls_state)

defp next_conn_state(ice_state, dtls_state)
when ice_state in [:new, :checking] and dtls_state in [:new, :connecting],
do: :connecting

defp next_conn_state(ice_state, dtls_state)
when ice_state in [:connected, :completed] and dtls_state in [:connected],
do: :connected

defp next_conn_state(ice_state, dtls_state) when ice_state == :failed or dtls_state == :failed,
do: :failed

defp next_conn_state(ice_state, dtls_state) when ice_state == :new and dtls_state == :new,
do: :new

defp update_conn_state(%{conn_state: conn_state} = state, conn_state), do: state

defp update_conn_state(state, new_conn_state) do
notify(state.owner, {:connectionstatechange, new_conn_state})
%{state | conn_state: new_conn_state}
end

defp set_description(:local, :answer, sdp, state) do
# NOTICE: internaly, we don't create SessionDescription
# as it would require serialization of sdp
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ defmodule ExWebRTC.MixProject do
[
{:ex_sdp, "~> 0.13"},
{:ex_ice, "~> 0.1"},
{:ex_dtls, "~> 0.14"},
{:ex_dtls, github: "elixir-webrtc/ex_dtls", branch: "verify-peer"},
{:ex_libsrtp, "~> 0.6"},
{:ex_rtp, "~> 0.2"},
{:ex_rtcp, "~> 0.1"},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
%{
"bunch": {:hex, :bunch, "1.6.0", "4775f8cdf5e801c06beed3913b0bd53fceec9d63380cdcccbda6be125a6cfd54", [:mix], [], "hexpm", "ef4e9abf83f0299d599daed3764d19e8eac5d27a5237e5e4d5e2c129cfeb9a22"},
"bunch_native": {:hex, :bunch_native, "0.5.0", "8ac1536789a597599c10b652e0b526d8833348c19e4739a0759a2bedfd924e63", [:mix], [{:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "24190c760e32b23b36edeb2dc4852515c7c5b3b8675b1a864e0715bdd1c8f80d"},
"bundlex": {:hex, :bundlex, "1.2.0", "a89869208a019376a38e8a10e1bd573dcbeae8addd381c2cd74e2817010bef8f", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, "~> 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "d2182b91a2a53847baadf4745ad2291853e786ad28671f474a611e7703dbca9b"},
"bundlex": {:hex, :bundlex, "1.3.1", "5791b4037df961f092eac9a51d8df91030a80381e442e580a3f4d82c9e5d34f0", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, "~> 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "9651ddc7e627dd1bd0eed9aaaba3de8b4bbc06c10980089f7276cdb82bb3fc51"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
"credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},
"dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"},
"earmark_parser": {:hex, :earmark_parser, "1.4.34", "b0fbb4fd333ee7e9babc07e9573796850759cd12796fcf2fec59cf0031cbaad9", [:mix], [], "hexpm", "cc0d7a6f2367e4504867b4ec38ceee24e89ee6bca9c7b94a6d940f54aba2e8d5"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"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.14.0", "f2e589a24396599551c6b142c3a6a7a55f7fa772c90716888ed42bf3c994cb2d", [:mix], [{:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "7fa79815d8dbcee3b1464c3590527fb85fae9592b6e092f2554c5974e2c5847a"},
"ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "d9d57bdc3726f2377a632664a43a8138b4faf7f4", [branch: "verify-peer"]},
"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_rctp": {:git, "https://github.com/elixir-webrtc/ex_rtcp.git", "c0cf2b7f995e34d13cee4cbb228376a55700fb6a", []},
Expand Down
Loading