Packages

HBCI/FinTS client library for Elixir.

Current section

Files

Jump to
fintex lib tan flicker_code.ex
Raw

lib/tan/flicker_code.ex

defmodule FinTex.Tan.FlickerCode do
@moduledoc false
# Flicker code extraction for optic chipTAN challenges.
#
# Based on Olaf Willuhn's Java implementation of flicker codes for HHD 1.3 and HHD 1.4,
# available at https://github.com/willuhn/hbci4java/blob/master/src/org/kapott/hbci/manager/FlickerCode.java
#
# Based in parts on Lars-Dominik Braun's JavaScript implementation of HHD 1.3 flicker codes,
# available at http://6xq.net/media/00/20/flickercode.html
alias FinTex.Helper.Checksum
alias FinTex.Helper.Conversion
alias FinTex.Tan.StartCode
alias FinTex.Tan.DataElement
import Checksum
import Conversion
@lc_length_hhd14 3
@lc_length_hhd13 2
@type t :: %__MODULE__{
lc: String.t,
start_code: StartCode.t,
data_elements: [DataElement.t]
}
defstruct [
:lc,
:start_code,
:data_elements
]
def new(code, version \\ :hhd14) when is_binary(code) and is_atom(version) do
c = code |> clean
len = case version do
:hhd14 -> @lc_length_hhd14
:hhd13 -> @lc_length_hhd13
end
{lc, c} = c |> String.split_at(len)
c_len = String.length(c)
case Integer.parse(lc) do
:error -> :error
{lc, _} when c_len == lc -> c |> do_parse(lc)
_ -> code |> new(:hhd13)
end
end
@spec render(t) :: String.t
def render(module) do
payload = create_payload(module)
payload <> luhn(module) <> xor(payload)
end
defp do_parse(code, lc) do
{start_code, code} = StartCode.new(code)
{data_element1, code} = DataElement.new(code)
{data_element2, code} = DataElement.new(code)
{data_element3, _} = DataElement.new(code)
%__MODULE__{
lc: lc,
start_code: start_code,
data_elements: [data_element1, data_element2, data_element3]
}
end
defp clean(code) when is_binary(code) do
code = code
|> String.replace(" ", "")
|> String.strip
case ~r/.*CHLGUC\d{4}(.*)CHLGTEXT.*/ |> Regex.run(code) do
nil -> code
matches -> "0" <> (matches |> Enum.at(1))
end
end
@spec create_payload(t) :: String.t
defp create_payload(%{start_code: %{control_bytes: control_bytes} = start_code, data_elements: data_elements}) do
payload = StartCode.render_length(start_code) <>
(control_bytes |> Enum.map(&to_hex/1) |> Enum.join) <>
StartCode.render_data(start_code)
append = for data_element <- data_elements,
length <- [DataElement.render_length(data_element, start_code.version)],
data <- [DataElement.render_data(data_element)]
do
length <> data
end |> Enum.join
payload = payload <> append
lc = (String.length(payload) + 2) |> div(2) |> to_hex(2)
lc <> payload
end
defp luhn(%{start_code: %{control_bytes: control_bytes} = start_code, data_elements: data_elements}) do
luhnsum = (StartCode.render_data(start_code) <>
(control_bytes |> Enum.map(&to_hex/1) |> Enum.join) <>
(data_elements |> Enum.map(&DataElement.render_data/1) |> Enum.join))
|> String.reverse
|> luhn(16, 10)
10 - luhnsum |> rem(10) |> to_hex(1)
end
end