Current section
Files
Jump to
Current section
Files
lib/hikvision/auth/digest_header_builder.ex
defmodule Hikvision.Auth.DigestHeaderBuilder do
@moduledoc false
defstruct realm: nil,
nonce: nil,
cnonce: nil,
qop: nil,
nc: "00000001",
method: nil,
uri: nil,
username: nil,
password: nil,
response: nil
def new(auth_header, http_method, uri, username, password) do
params = parse_header(auth_header)
%__MODULE__{
realm: params["realm"],
nonce: params["nonce"],
qop: params["qop"],
cnonce: :crypto.strong_rand_bytes(32) |> Base.encode16(case: :lower),
method: http_method,
uri: uri,
username: username,
password: password
}
end
def calculate(%__MODULE__{qop: qop, cnonce: cnonce, nonce: nonce, nc: nc} = builder) do
a1 = hash([builder.username, ":", builder.realm, ":", builder.password])
a2 = hash([String.upcase("#{builder.method}"), ":", builder.uri])
[a1, nonce, nc, cnonce, qop, a2]
|> Enum.join(":")
|> hash()
|> then(&%__MODULE__{builder | response: &1})
end
def build(%__MODULE__{} = builder) do
builder
|> Map.from_struct()
|> Map.drop([:method, :password])
|> Enum.map_join(", ", fn
{key, value} when key in [:qop, :nc] -> "#{key}=#{value}"
{key, value} -> "#{key}=\"#{value}\""
end)
|> then(&"Digest #{&1}")
end
defp parse_header(header) do
["Digest", params] = String.split(header, " ", parts: 2)
params
|> String.split(", ")
|> Enum.map(fn param ->
[key, value] = String.split(param, "=")
{key, String.trim(value, "\"")}
end)
|> Map.new()
end
defp hash(value), do: :crypto.hash(:md5, value) |> Base.encode16(case: :lower)
end