diff --git a/lib/chronoscope/nts.ex b/lib/chronoscope/nts.ex index a9df84a..d03e05b 100644 --- a/lib/chronoscope/nts.ex +++ b/lib/chronoscope/nts.ex @@ -11,6 +11,7 @@ defmodule Chronoscope.NTS do case :ssl.connect(host, port, tls_options, @timeout_in_milliseconds) do {:ok, socket} -> perform_key_establishment(socket) {:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, to_string(error)} + {:error, :timeout} -> {:error, :timeout} {:error, error} -> {:error, inspect(error)} error -> {:error, inspect(error)} end diff --git a/lib/chronoscope/nts/key_establishment.ex b/lib/chronoscope/nts/key_establishment.ex index fc00b51..3c405ac 100644 --- a/lib/chronoscope/nts/key_establishment.ex +++ b/lib/chronoscope/nts/key_establishment.ex @@ -10,24 +10,37 @@ defmodule Chronoscope.NTS.KeyEstablishment do 30 => "AEAD_AES_128_GCM_SIV" } - @next_protocols %{0 => "NTPv4"} + @next_protocols %{ + 0 => "NTPv4" + } + + @errors %{ + 0 => "Unrecognized Critical Record", + 1 => "Bad Request", + 2 => "Internal Server Error" + } def request() do @next_protocol_negotiation <> @aead_algorithm_negotiation <> @end_of_message end def parse_response(response) do - {:ok, Map.update(do_parse_response(response, %{}), :cookies, 0, &length/1)} + response + |> do_parse_response(%{}) + |> Map.update(:cookies, 0, &length/1) + |> then(&{:ok, &1}) end defp do_parse_response([], acc) do acc end + # End of Message defp do_parse_response([0x80, 0x00, 0x00, 0x00], acc) do acc end + # NTS Next Protocol Negotiation defp do_parse_response([0x80, 0x01, length_high, length_low | rest], acc) do length = combine_octets(length_high, length_low) {next_protocols, remaining} = Enum.split(rest, length) @@ -38,6 +51,24 @@ defmodule Chronoscope.NTS.KeyEstablishment do ) end + # Error + defp do_parse_response([0x80, 0x02, 0x00, 0x02, error_high, error_low | rest], acc) do + error = combine_octets(error_high, error_low) + + do_parse_response( + rest, + Map.put(acc, :error, Map.get(@errors, error, error)) + ) + end + + # Warning + defp do_parse_response([0x80, 0x03, 0x00, 0x02, warning_high, warning_low | rest], acc) do + warning = combine_octets(warning_high, warning_low) + + do_parse_response(rest, Map.put(acc, :warning, warning)) + end + + # AEAD Algorithm Negotiation defp do_parse_response([first, 0x04, length_high, length_low | rest], acc) when first == 0x00 or first == 0x80 do length = combine_octets(length_high, length_low) @@ -49,6 +80,7 @@ defmodule Chronoscope.NTS.KeyEstablishment do ) end + # New Cookie for NTPv4 defp do_parse_response([first, 0x05, length_high, length_low | rest], acc) when first == 0x00 or first == 0x80 do length = combine_octets(length_high, length_low) @@ -62,6 +94,23 @@ defmodule Chronoscope.NTS.KeyEstablishment do ) end + # NTPv4 Server Negotiation + defp do_parse_response([first, 0x06, length_high, length_low | rest], acc) + when first == 0x00 or first == 0x80 do + length = combine_octets(length_high, length_low) + {server, remaining} = Enum.split(rest, length) + + do_parse_response(remaining, Map.put(acc, :server, to_string(server))) + end + + # NTPv4 Port Negotiation + defp do_parse_response([first, 0x07, 0x00, 0x02, port_high, port_low | rest], acc) + when first == 0x00 or first == 0x80 do + port = combine_octets(port_high, port_low) + + do_parse_response(rest, Map.put(acc, :port, port)) + end + defp do_parse_response([_, _, length_high, length_low | rest], acc) do length = combine_octets(length_high, length_low) {_, remaining} = Enum.split(rest, length) @@ -97,8 +146,6 @@ defmodule Chronoscope.NTS.KeyEstablishment do |> then(&do_parse_aead_algorithm_list(rest, [&1 | acc])) end - # todo parse server/port information - defp combine_octets(high, low) do high <<< 8 ||| low end diff --git a/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex b/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex index 679d8ec..80d6384 100644 --- a/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex +++ b/lib/chronoscope_web/controllers/api/v1/nts/key_establishment_controller.ex @@ -16,7 +16,7 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentController do ok_response(conn, %{status: :ok, configuration: configuration}) {:error, error} -> - ok_response(conn, %{status: :error, reason: inspect(error)}) + ok_response(conn, %{status: :error, reason: to_string(error)}) end end