Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/exmbus/crypto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ defmodule Exmbus.Crypto do
iv :: iodata(),
data :: iodata(),
flag_or_options :: crypto_opts() | boolean()
) :: {:ok, binary} | {:error, crypto_error()}
) :: {:ok, binary} | {:error, {:crypto_error, crypto_error()}}
def crypto_one_time(cipher, key, iv, data, flag_or_options) do
try do
{:ok, :crypto.crypto_one_time(cipher, key, iv, data, flag_or_options)}
catch
:error, {_tag, _c_file_info, _description} = e -> {:error, e}
:error, {_tag, _c_file_info, _description} = e -> {:error, {:crypto_error, e}}
end
Comment thread
tudborg marked this conversation as resolved.
end

Expand All @@ -43,7 +43,7 @@ defmodule Exmbus.Crypto do
key :: iodata(),
data :: iodata(),
flag_or_options :: crypto_opts() | boolean()
) :: {:ok, binary} | {:error, crypto_error()}
) :: {:ok, binary} | {:error, {:crypto_error, crypto_error()}}
def crypto_one_time(cipher, key, data, flag_or_options) do
try do
{:ok, :crypto.crypto_one_time(cipher, key, data, flag_or_options)}
Expand Down
22 changes: 16 additions & 6 deletions lib/exmbus/parser/afl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,23 @@ defmodule Exmbus.Parser.Afl do
# see table in module doc
# we can parse up to the MAC
def parse(%{bin: <<0x90, afll::8, afl_bytes::binary-size(afll), rest::binary>>} = ctx) do
<<fcl_bytes::binary-size(2), bytes::binary>> = afl_bytes
{:ok, fcl} = FragmentationControlField.decode(fcl_bytes)
with <<fcl_bytes::binary-size(2), bytes::binary>> <- afl_bytes,
{:ok, fcl} <- FragmentationControlField.decode(fcl_bytes) do
parse_fields(ctx, fcl, bytes, rest)
else
_ -> {:halt, Context.add_error(ctx, {:invalid_afl, :truncated})}
end
end

def parse(%{bin: <<0x90, _rest::binary>>} = ctx) do
{:halt, Context.add_error(ctx, {:invalid_afl, :truncated})}
end

def parse(%{bin: <<ci, _rest::binary>>} = ctx) do
{:halt, Context.add_error(ctx, {:ci_not_afl, ci})}
end

defp parse_fields(ctx, fcl, bytes, rest) do
# our "accumulator" for the AFL layer
afl = %__MODULE__{fcl: fcl}

Expand Down Expand Up @@ -119,10 +133,6 @@ defmodule Exmbus.Parser.Afl do
end
end

def parse(%{bin: <<ci, _rest::binary>>} = ctx) do
{:halt, Context.add_error(ctx, {:ci_not_afl, ci})}
end

@doc """
Verifies the MAC of the AFL.
NOTE: This handler should be attached _after_ the TPL has been parsed,
Expand Down
54 changes: 36 additions & 18 deletions lib/exmbus/parser/dll/mbus.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ defmodule Exmbus.Parser.Dll.Mbus do
end
end

defp _parse(<<c::binary-size(1), a, rest::binary>>, ctx) do
{:ok, control} = decode_c_field(c)

dll = %__MODULE__{
control: control,
address: a
}
def parse(ctx), do: {:halt, Context.add_error(ctx, {:invalid_dll, :mbus})}

{:next, %{ctx | dll: dll, bin: rest}}
defp _parse(<<c::binary-size(1), a, rest::binary>>, ctx) do
with {:ok, control} <- decode_c_field(c) do
dll = %__MODULE__{
control: control,
address: a
}

{:next, %{ctx | dll: dll, bin: rest}}
else
{:error, reason} -> {:halt, Context.add_error(ctx, reason)}
end
end

def unparse(%{dll: nil} = ctx) do
Expand Down Expand Up @@ -69,24 +73,38 @@ defmodule Exmbus.Parser.Dll.Mbus do
def direction(%__MODULE__{control: :rsp_ud}), do: {:ok, :from_meter}

defp decode_c_field(<<0::1, 1::1, _fcb::1, _fcv::1, 0x0::4>>),
do: raise("SND-NKE not implemented")
do: {:error, {:not_implemented, :snd_nke}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, _fcv::1, 0x3::4>>),
do: raise("SND-UD/SND-UD2 not implemented")
do: {:error, {:not_implemented, :snd_ud}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x4::4>>), do: {:ok, :snd_nr}
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x5::4>>), do: raise("SND-UD3 not implemented")

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x5::4>>),
do: {:error, {:not_implemented, :snd_ud3}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x6::4>>), do: {:ok, :snd_ir}
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x7::4>>), do: raise("ACC-NR not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x8::4>>), do: raise("ACC-DMD not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xA::4>>), do: raise("REQ-UD1 not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xB::4>>), do: raise("REQ-UD2 not implemented")

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x0::4>>), do: raise("ACK not implemented")
defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x1::4>>), do: raise("NACK not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x7::4>>),
do: {:error, {:not_implemented, :acc_nr}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x8::4>>),
do: {:error, {:not_implemented, :acc_dmd}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xA::4>>),
do: {:error, {:not_implemented, :req_ud1}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xB::4>>),
do: {:error, {:not_implemented, :req_ud2}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x0::4>>),
do: {:error, {:not_implemented, :ack}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x1::4>>),
do: {:error, {:not_implemented, :nack}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x6::4>>),
do: raise("CNF-IR not implemented")
do: {:error, {:not_implemented, :cnf_ir}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x8::4>>), do: {:ok, :rsp_ud}
end
36 changes: 26 additions & 10 deletions lib/exmbus/parser/dll/wmbus.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ defmodule Exmbus.Parser.Dll.Wmbus do
end
end

defp do_parse(ctx), do: {:halt, Context.add_error(ctx, {:invalid_dll, :wmbus})}

def unparse(%{dll: nil} = ctx) do
{:next, ctx}
end
Expand Down Expand Up @@ -137,24 +139,38 @@ defmodule Exmbus.Parser.Dll.Wmbus do
def direction(%__MODULE__{control: :rsp_ud}), do: {:ok, :from_meter}

defp decode_c_field(<<0::1, 1::1, _fcb::1, _fcv::1, 0x0::4>>),
do: raise("SND-NKE not implemented")
do: {:error, {:not_implemented, :snd_nke}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, _fcv::1, 0x3::4>>),
do: raise("SND-UD/SND-UD2 not implemented")
do: {:error, {:not_implemented, :snd_ud}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x4::4>>), do: {:ok, :snd_nr}
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x5::4>>), do: raise("SND-UD3 not implemented")

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x5::4>>),
do: {:error, {:not_implemented, :snd_ud3}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x6::4>>), do: {:ok, :snd_ir}
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x7::4>>), do: raise("ACC-NR not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x8::4>>), do: raise("ACC-DMD not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xA::4>>), do: raise("REQ-UD1 not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xB::4>>), do: raise("REQ-UD2 not implemented")

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x0::4>>), do: raise("ACK not implemented")
defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x1::4>>), do: raise("NACK not implemented")
defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x7::4>>),
do: {:error, {:not_implemented, :acc_nr}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 0::1, 0x8::4>>),
do: {:error, {:not_implemented, :acc_dmd}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xA::4>>),
do: {:error, {:not_implemented, :req_ud1}}

defp decode_c_field(<<0::1, 1::1, _fcb::1, 1::1, 0xB::4>>),
do: {:error, {:not_implemented, :req_ud2}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x0::4>>),
do: {:error, {:not_implemented, :ack}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x1::4>>),
do: {:error, {:not_implemented, :nack}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x6::4>>),
do: raise("CNF-IR not implemented")
do: {:error, {:not_implemented, :cnf_ir}}

defp decode_c_field(<<0::1, 0::1, _acd::1, _dfc::1, 0x8::4>>), do: {:ok, :rsp_ud}

Expand Down
60 changes: 35 additions & 25 deletions lib/exmbus/parser/ell.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ defmodule Exmbus.Parser.Ell do
# > Table 44 below, shows the complete extension block in this case.
# Fields: CC, ACC
def parse(%{bin: <<0x8C, cc::binary-size(1), acc, rest::binary>>} = ctx) do
{:ok, control} = CommunicationControl.decode(cc)
with {:ok, control} <- CommunicationControl.decode(cc) do
ell = %Unencrypted{
communication_control: control,
access_no: acc
}

ell = %Unencrypted{
communication_control: control,
access_no: acc
}

{:next, %{ctx | ell: ell, bin: rest}}
{:next, %{ctx | ell: ell, bin: rest}}
else
{:error, reason} -> {:halt, Context.add_error(ctx, {:ell_parse_error, reason, ci: 0x8C})}
end
end

# > This value of the CI-field is used if data encryption at the link layer is used in the frame.
Expand All @@ -58,16 +60,18 @@ defmodule Exmbus.Parser.Ell do
rest::binary>>
} = ctx
) do
{:ok, control} = CommunicationControl.decode(cc)
{:ok, session_number} = SessionNumber.decode(sn)

ell = %Encrypted{
communication_control: control,
access_no: acc,
session_number: session_number
}

{:next, %{ctx | ell: ell, bin: <<payload_crc::size(16), rest::binary>>}}
with {:ok, control} <- CommunicationControl.decode(cc),
{:ok, session_number} <- SessionNumber.decode(sn) do
ell = %Encrypted{
communication_control: control,
access_no: acc,
session_number: session_number
}

{:next, %{ctx | ell: ell, bin: <<payload_crc::size(16), rest::binary>>}}
else
{:error, reason} -> {:halt, Context.add_error(ctx, {:ell_parse_error, reason, ci: 0x8D})}
end
end

# > This value of the CI-field is used if data encryption at the link layer is not used in the frame.
Expand All @@ -87,20 +91,26 @@ defmodule Exmbus.Parser.Ell do
# > This extended link layer specifies the receiver address.
# > Table 47 below shows the complete extension block in this case.
# Fields: CC, ACC, M2, A2, SN, PayloadCRC
def parse(%{
bin:
<<0x8F, _cc::binary-size(1), _acc, _m2::binary-size(2), _a2::binary-size(6),
_sn::binary-size(4), _payload_crc::binary-size(2), _rest::binary>>
}) do
raise "TODO: ELL IV"
def parse(
%{
bin:
<<0x8F, _cc::binary-size(1), _acc, _m2::binary-size(2), _a2::binary-size(6),
_sn::binary-size(4), _payload_crc::binary-size(2), _rest::binary>>
} = ctx
) do
{:halt, Context.add_error(ctx, {:not_implemented, :ell_iv})}
end

# > The variable Extended Link Layer allows to select optional ELL fields separately.
# > The shadowed rows of Table 48 shall always be present.
# > The other fields are optional and can be selected in case they are needed.
# > The table defines the ordering of the fields.
def parse(%{bin: <<0x86, _rest::binary>>}) do
raise "TODO: ELL V"
def parse(%{bin: <<0x86, _rest::binary>>} = ctx) do
{:halt, Context.add_error(ctx, {:not_implemented, :ell_v})}
end

def parse(%{bin: <<ci, _rest::binary>>} = ctx) when ci in [0x8C, 0x8D, 0x8E, 0x8F] do
{:halt, Context.add_error(ctx, {:ell_parse_error, :truncated, ci: ci})}
end

def parse(%{bin: <<ci, _rest::binary>>} = ctx) do
Expand Down
4 changes: 3 additions & 1 deletion lib/exmbus/parser/ell/session_number.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule Exmbus.Parser.Ell.SessionNumber do
:aes_128_ctr

enc ->
raise "ELL session number encryption mode #{enc} is reserved for future use, see 13.2.11 of EN 13757-4:2019"
throw({:error, {:reserved_ell_session_encryption_mode, enc}})
end

{:ok,
Expand All @@ -35,6 +35,8 @@ defmodule Exmbus.Parser.Ell.SessionNumber do
# inter-minute session
session: session
}}
catch
{:error, _} = e -> e
end

def encode(%__MODULE__{encryption: mode, minutes: m, session: s}) do
Expand Down
Loading
Loading