diff --git a/lib/chronoscope/nts/key_establishment.ex b/lib/chronoscope/nts/key_establishment.ex index 5e80e0e..677ba73 100644 --- a/lib/chronoscope/nts/key_establishment.ex +++ b/lib/chronoscope/nts/key_establishment.ex @@ -25,10 +25,7 @@ defmodule Chronoscope.NTS.KeyEstablishment do end def parse_response(response) do - response - |> do_parse_response(%{}) - |> Map.update(:cookies, 0, &length/1) - |> then(&{:ok, &1}) + {:ok, do_parse_response(response, %{})} end defp do_parse_response([], acc) do 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 80d6384..a593cae 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 @@ -13,16 +13,16 @@ defmodule ChronoscopeWeb.API.V1.NTS.KeyEstablishmentController do case NTS.key_establishment(host, port) do {:ok, configuration} -> - ok_response(conn, %{status: :ok, configuration: configuration}) + json(conn, %{status: :ok, configuration: format_configuration(configuration)}) {:error, error} -> - ok_response(conn, %{status: :error, reason: to_string(error)}) + json(conn, %{status: :error, reason: to_string(error)}) end end - defp ok_response(conn, body) do - conn - |> Plug.Conn.put_resp_content_type("application/json") - |> Plug.Conn.send_resp(:ok, Jason.encode!(body)) + defp format_configuration(configuration) do + configuration + |> Map.take([:aead_algorithms, :cookie_length, :cookies, :next_protocols, :port, :server]) + |> Map.update(:cookies, 0, &length/1) end end diff --git a/test/chronoscope/nts/key_establishment_test.exs b/test/chronoscope/nts/key_establishment_test.exs index 70f112a..c1e126a 100644 --- a/test/chronoscope/nts/key_establishment_test.exs +++ b/test/chronoscope/nts/key_establishment_test.exs @@ -9,96 +9,99 @@ defmodule Chronoscope.NTS.KeyEstablishmentTest do end test "handles empty response" do - assert KeyEstablishment.parse_response([]) == {:ok, %{cookies: 0}} + assert KeyEstablishment.parse_response([]) == {:ok, %{}} + end + + test "ignores unkown record" do + assert KeyEstablishment.parse_response([0x80, 0x21, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03]) == {:ok, %{}} end test "handles end of message record" do - assert KeyEstablishment.parse_response([0x80, 0x00, 0x00, 0x00]) == {:ok, %{cookies: 0}} + assert KeyEstablishment.parse_response([0x80, 0x00, 0x00, 0x00]) == {:ok, %{}} end test "handles next protocol negotiation record" do - assert KeyEstablishment.parse_response([0x80, 0x01, 0x00, 0x02, 0x00, 0x00]) == - {:ok, %{cookies: 0, next_protocols: ["NTPv4"]}} + assert KeyEstablishment.parse_response([0x80, 0x01, 0x00, 0x02, 0x00, 0x00]) == {:ok, %{next_protocols: ["NTPv4"]}} end test "does not handle next protocol negotiation record without critical bit" do - assert KeyEstablishment.parse_response([0x00, 0x01, 0x00, 0x02, 0x00, 0x00]) == {:ok, %{cookies: 0}} + assert KeyEstablishment.parse_response([0x00, 0x01, 0x00, 0x02, 0x00, 0x00]) == {:ok, %{}} end test "handles empty next protocols" do - assert KeyEstablishment.parse_response([0x80, 0x01, 0x00, 0x00]) == {:ok, %{cookies: 0, next_protocols: []}} + assert KeyEstablishment.parse_response([0x80, 0x01, 0x00, 0x00]) == {:ok, %{next_protocols: []}} end test "handles multiple next protocols" do assert KeyEstablishment.parse_response([0x80, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]) == - {:ok, %{cookies: 0, next_protocols: ["NTPv4", "UNASSIGNED", "NTPv4"]}} + {:ok, %{next_protocols: ["NTPv4", "UNASSIGNED", "NTPv4"]}} end test "handles aead algorithm negotiation record" do assert KeyEstablishment.parse_response([0x80, 0x04, 0x00, 0x02, 0x00, 0x0F]) == - {:ok, %{cookies: 0, aead_algorithms: ["AEAD_AES_SIV_CMAC_256"]}} + {:ok, %{aead_algorithms: ["AEAD_AES_SIV_CMAC_256"]}} end test "handles aead algorithm negotiation record without critical bit" do assert KeyEstablishment.parse_response([0x00, 0x04, 0x00, 0x02, 0x00, 0x1E]) == - {:ok, %{cookies: 0, aead_algorithms: ["AEAD_AES_128_GCM_SIV"]}} + {:ok, %{aead_algorithms: ["AEAD_AES_128_GCM_SIV"]}} end test "handles empty aead algorithms" do - assert KeyEstablishment.parse_response([0x80, 0x04, 0x00, 0x00]) == {:ok, %{cookies: 0, aead_algorithms: []}} + assert KeyEstablishment.parse_response([0x80, 0x04, 0x00, 0x00]) == {:ok, %{aead_algorithms: []}} end test "handles multiple aead algorithms" do assert KeyEstablishment.parse_response([0x80, 0x04, 0x00, 0x06, 0x00, 0x1E, 0x00, 0x01, 0x00, 0x0F]) == - {:ok, %{cookies: 0, aead_algorithms: ["AEAD_AES_SIV_CMAC_256", "UNKNOWN", "AEAD_AES_128_GCM_SIV"]}} + {:ok, %{aead_algorithms: ["AEAD_AES_SIV_CMAC_256", "UNKNOWN", "AEAD_AES_128_GCM_SIV"]}} end test "handles error record" do - assert KeyEstablishment.parse_response([0x80, 0x02, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{cookies: 0, error: "Bad Request"}} + assert KeyEstablishment.parse_response([0x80, 0x02, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{error: "Bad Request"}} end test "handles unknown error record" do - assert KeyEstablishment.parse_response([0x80, 0x02, 0x00, 0x02, 0x00, 0x99]) == {:ok, %{cookies: 0, error: "153"}} + assert KeyEstablishment.parse_response([0x80, 0x02, 0x00, 0x02, 0x00, 0x99]) == {:ok, %{error: "153"}} end test "does not handle error record without critical bit" do - assert KeyEstablishment.parse_response([0x00, 0x02, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{cookies: 0}} + assert KeyEstablishment.parse_response([0x00, 0x02, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{}} end test "handles warning record" do - assert KeyEstablishment.parse_response([0x80, 0x03, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{cookies: 0, warning: "1"}} + assert KeyEstablishment.parse_response([0x80, 0x03, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{warning: "1"}} end test "does not handle warning record without critical bit" do - assert KeyEstablishment.parse_response([0x00, 0x03, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{cookies: 0}} + assert KeyEstablishment.parse_response([0x00, 0x03, 0x00, 0x02, 0x00, 0x01]) == {:ok, %{}} end test "handles server record" do assert KeyEstablishment.parse_response([0x80, 0x06, 0x00, 0x09, ?1, ?2, ?7, ?., ?0, ?., ?0, ?., ?1]) == - {:ok, %{cookies: 0, server: "127.0.0.1"}} + {:ok, %{server: "127.0.0.1"}} end test "handles server record without critical bit" do assert KeyEstablishment.parse_response([0x00, 0x06, 0x00, 0x09, ?1, ?2, ?7, ?., ?0, ?., ?0, ?., ?1]) == - {:ok, %{cookies: 0, server: "127.0.0.1"}} + {:ok, %{server: "127.0.0.1"}} end test "handles port record" do - assert KeyEstablishment.parse_response([0x80, 0x07, 0x00, 0x02, 0x04, 0xCE]) == {:ok, %{cookies: 0, port: 1230}} + assert KeyEstablishment.parse_response([0x80, 0x07, 0x00, 0x02, 0x04, 0xCE]) == {:ok, %{port: 1230}} end test "handles port record without critical bit" do - assert KeyEstablishment.parse_response([0x00, 0x07, 0x00, 0x02, 0x04, 0xCE]) == {:ok, %{cookies: 0, port: 1230}} + assert KeyEstablishment.parse_response([0x00, 0x07, 0x00, 0x02, 0x04, 0xCE]) == {:ok, %{port: 1230}} end test "handles cookie record" do assert KeyEstablishment.parse_response([0x80, 0x05, 0x00, 0x0E, ?c, ?h, ?o, ?c, ?o, ?l, ?a, ?t, ?e, ?_, ?c, ?h, ?i, ?p]) == - {:ok, %{cookies: 1, cookie_length: 14}} + {:ok, %{cookies: ['chocolate_chip'], cookie_length: 14}} end test "handles cookie record without critical bit" do assert KeyEstablishment.parse_response([0x00, 0x05, 0x00, 0x0E, ?c, ?h, ?o, ?c, ?o, ?l, ?a, ?t, ?e, ?_, ?c, ?h, ?i, ?p]) == - {:ok, %{cookies: 1, cookie_length: 14}} + {:ok, %{cookies: ['chocolate_chip'], cookie_length: 14}} end test "sets cookie length to longest cookie" do @@ -107,7 +110,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentTest do [0x80, 0x05, 0x00, 0x03, ?c, ?c, ?c] ++ [0x80, 0x05, 0x00, 0x01, ?c] ) == - {:ok, %{cookies: 3, cookie_length: 3}} + {:ok, %{cookies: ['c', 'ccc', 'c'], cookie_length: 3}} end test "handles full response" do @@ -115,13 +118,14 @@ defmodule Chronoscope.NTS.KeyEstablishmentTest do [0x80, 0x01, 0x00, 0x02, 0x00, 0x00] ++ [0x80, 0x04, 0x00, 0x06, 0x00, 0x1E, 0x00, 0x01, 0x00, 0x0F] ++ [0x80, 0x05, 0x00, 0x01, ?c] ++ + [0x00, 0x21, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03] ++ [0x80, 0x06, 0x00, 0x09, ?1, ?2, ?7, ?., ?0, ?., ?0, ?., ?1] ++ [0x80, 0x07, 0x00, 0x02, 0x04, 0xCE] ++ [0x80, 0x00, 0x00, 0x00] ) == {:ok, %{ - cookies: 1, + cookies: ['c'], aead_algorithms: ["AEAD_AES_SIV_CMAC_256", "UNKNOWN", "AEAD_AES_128_GCM_SIV"], cookie_length: 1, next_protocols: ["NTPv4"], @@ -133,6 +137,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentTest do test "doesn't read past end of message record" do assert KeyEstablishment.parse_response( [0x80, 0x01, 0x00, 0x02, 0x00, 0x00] ++ + [0x00, 0x21, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03] ++ [0x80, 0x04, 0x00, 0x02, 0x00, 0x1E] ++ [0x80, 0x00, 0x00, 0x00] ++ [0x80, 0x05, 0x00, 0x01, ?c] ++ @@ -141,7 +146,6 @@ defmodule Chronoscope.NTS.KeyEstablishmentTest do ) == {:ok, %{ - cookies: 0, aead_algorithms: ["AEAD_AES_128_GCM_SIV"], next_protocols: ["NTPv4"] }}