From ab8a31bad4bcdba7e7e8e74d23c68217ef291a11 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Sat, 16 Mar 2024 14:05:30 -0400 Subject: [PATCH] Send an NtS key exchange and receive the response --- config/dev.exs | 2 +- lib/chronoscope/gemini.ex | 3 + lib/chronoscope/nts.ex | 3 + lib/chronoscope/nts/key_exchange.ex | 2 + .../api/nts/key_exchange_controller.ex | 63 +++++++++++++++++++ lib/chronoscope_web/router.ex | 9 +-- mix.exs | 3 +- mix.lock | 2 + 8 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 lib/chronoscope/gemini.ex create mode 100644 lib/chronoscope/nts.ex create mode 100644 lib/chronoscope/nts/key_exchange.ex create mode 100644 lib/chronoscope_web/controllers/api/nts/key_exchange_controller.ex diff --git a/config/dev.exs b/config/dev.exs index be4fdda..47a873d 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -12,7 +12,7 @@ config :chronoscope, ChronoscopeWeb.Endpoint, http: [ip: {127, 0, 0, 1}, port: 4000], check_origin: false, code_reloader: true, - debug_errors: true, + debug_errors: false, secret_key_base: "rp86O1DRHYfJbxHoM3ZyauXwsduX2034Ywv/7sgJtqMoPJdDXRm/219DcCW5q/vD", watchers: [ esbuild: {Esbuild, :install_and_run, [:chronoscope, ~w(--sourcemap=inline --watch)]}, diff --git a/lib/chronoscope/gemini.ex b/lib/chronoscope/gemini.ex new file mode 100644 index 0000000..4ace4db --- /dev/null +++ b/lib/chronoscope/gemini.ex @@ -0,0 +1,3 @@ +defmodule Chronoscope.Gemini do + +end diff --git a/lib/chronoscope/nts.ex b/lib/chronoscope/nts.ex new file mode 100644 index 0000000..ef72037 --- /dev/null +++ b/lib/chronoscope/nts.ex @@ -0,0 +1,3 @@ +defmodule Chronoscope.NTS do + +end diff --git a/lib/chronoscope/nts/key_exchange.ex b/lib/chronoscope/nts/key_exchange.ex new file mode 100644 index 0000000..c3bf464 --- /dev/null +++ b/lib/chronoscope/nts/key_exchange.ex @@ -0,0 +1,2 @@ +defmodule Chronoscope.NTS.KeyExchange do +end diff --git a/lib/chronoscope_web/controllers/api/nts/key_exchange_controller.ex b/lib/chronoscope_web/controllers/api/nts/key_exchange_controller.ex new file mode 100644 index 0000000..07735f4 --- /dev/null +++ b/lib/chronoscope_web/controllers/api/nts/key_exchange_controller.ex @@ -0,0 +1,63 @@ +defmodule ChronoscopeWeb.API.NTS.KeyExchangeController do + use ChronoscopeWeb, :controller + + require Logger + + @default_port "4460" + @timeout_in_milliseconds 3000 + + def get(conn, params) do + host = to_charlist(params["host"]) + port = String.to_integer(params["port"] || @default_port) + + tls_options = :tls_certificate_check.options(host) ++ [alpn_advertised_protocols: ["ntse/1"]] + + case :ssl.connect(host, port, tls_options, @timeout_in_milliseconds) do + {:ok, socket} -> + key_exchange_request = + <<0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0x80, 0x04, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x0F, + 0x80, 0x00, 0x00, 0x00>> + + :ok = :ssl.send(socket, key_exchange_request) + + receive do + {:ssl, _socket, data} -> + :ssl.close(socket) + ok_response(conn, %{status: :ok, response: parse_response(data)}) + + msg -> + :ssl.close(socket) + Logger.error("received unexpected message: #{inspect(msg)}") + ok_response(conn, %{status: :error, reason: :no_response}) + after + @timeout_in_milliseconds -> + :ssl.close(socket) + Logger.error("timed out waiting for response") + ok_response(conn, %{status: :error, reason: :timeout}) + end + + {:error, {:tls_alert, {:handshake_failure, error}}} -> + ok_response(conn, %{status: :error, reason: to_string(error)}) + + {:error, error} -> + ok_response(conn, %{status: :error, reason: inspect(error)}) + + error -> + ok_response(conn, %{status: :error, reason: inspect(error)}) + end + end + + defp parse_response([0x80 | _] = _data) do + "success" + end + + defp parse_response(_data) do + "failure" + end + + defp ok_response(conn, body) do + conn + |> Plug.Conn.put_resp_content_type("application/json") + |> Plug.Conn.send_resp(:ok, Jason.encode!(body)) + end +end diff --git a/lib/chronoscope_web/router.ex b/lib/chronoscope_web/router.ex index 4e3fd45..93bd578 100644 --- a/lib/chronoscope_web/router.ex +++ b/lib/chronoscope_web/router.ex @@ -20,10 +20,11 @@ defmodule ChronoscopeWeb.Router do get "/", PageController, :home end - # Other scopes may use custom stacks. - # scope "/api", ChronoscopeWeb do - # pipe_through :api - # end + scope "/api", ChronoscopeWeb.API do + pipe_through :api + + get "/key-exchange", NTS.KeyExchangeController, :get + end # Enable LiveDashboard and Swoosh mailbox preview in development if Application.compile_env(:chronoscope, :dev_routes) do diff --git a/mix.exs b/mix.exs index baf20ce..11484de 100644 --- a/mix.exs +++ b/mix.exs @@ -54,7 +54,8 @@ defmodule Chronoscope.MixProject do {:gettext, "~> 0.20"}, {:jason, "~> 1.2"}, {:dns_cluster, "~> 0.1.1"}, - {:bandit, "~> 1.2"} + {:bandit, "~> 1.2"}, + {:tls_certificate_check, "~> 1.21"} ] end diff --git a/mix.lock b/mix.lock index e955839..49c2d6b 100644 --- a/mix.lock +++ b/mix.lock @@ -24,12 +24,14 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "swoosh": {:hex, :swoosh, "1.15.3", "5c3f05b6e4e08cd4f75ad7e90db3c2b73c9d9de00503cce36a694951c2d69185", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "97a667b96ca8cc48a4679f6cd1f40a36d8701cf052587298473614caa70f164a"}, "tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"}, + "tls_certificate_check": {:hex, :tls_certificate_check, "1.21.0", "042ab2c0c860652bc5cf69c94e3a31f96676d14682e22ec7813bd173ceff1788", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6cee6cffc35a390840d48d463541d50746a7b0e421acaadb833cfc7961e490e7"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"}, }