Current section
Files
Jump to
Current section
Files
lib/ex_mobile_device/ssl.ex
defmodule ExMobileDevice.Ssl do
@moduledoc """
SSL/TLS helper functions.
This module contains a helper function, `connect/4` that can be used
to authenticate an _existing_ `:gen_tcp` socket.
"""
@legacy_sign_algos [
sha: :rsa,
sha: :ecdsa,
sha256: :rsa,
sha256: :ecdsa,
sha384: :rsa,
sha384: :ecdsa,
sha512: :rsa,
sha512: :ecdsa
]
require Logger
@doc """
Authenticate an existing connection, using a pair-record.
On success, returns an `:ssl` socket.
"""
@spec connect(port(), map(), String.t(), timeout()) :: {:ok, :ssl.sslsocket()} | {:error, any()}
def connect(socket, pair_record, udid, timeout \\ :infinity)
when is_port(socket) and is_map(pair_record) do
host_cert = pair_record["HostCertificate"]
host_key = pair_record["HostPrivateKey"]
[{_, cert, _}] = :public_key.pem_decode(host_cert)
[{key_type, key, _}] = :public_key.pem_decode(host_key)
debug_ssl? =
case :ets.lookup(__MODULE__, udid) do
[{_, prev_cert, prev_key, debug}] ->
if prev_cert != host_cert do
Logger.error(
"#{__MODULE__}: #{udid} HostCertificate mismatch: Prev: #{prev_cert}. New: #{host_cert}"
)
end
if prev_key != host_key do
Logger.error("#{__MODULE__}: #{udid} HostPrivateKey mismatch")
end
debug
_ ->
false
end
result =
:ssl.connect(
socket,
[
cert: cert,
key: {key_type, key},
verify: :verify_none,
signature_algs: signature_algs(),
log_level: if(debug_ssl?, do: :debug, else: :notice)
],
timeout
)
# Insert if new otherwise just update the pair record
if !:ets.insert_new(__MODULE__, {udid, host_cert, host_key, false}) do
:ets.update_element(__MODULE__, udid, [{2, host_cert}, {3, host_key}])
end
result
end
def init do
:ets.new(__MODULE__, [:public, :named_table])
:ok
end
defp signature_algs do
:ssl.signature_algs(:default, :"tlsv1.3") ++ @legacy_sign_algos
end
end