Giter Site home page Giter Site logo

one_time_pass_ecto's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

one_time_pass_ecto's Issues

Add docs + examples

Hi,

Would it be possible to add some examples on how to integrate this with existing applications in the README?
Is it also possible to add some code examples in an examples/ directory?
Thanks in advance! ๐Ÿ˜„

`last` needs to be 1 or greater or the library doesn't work

If you use 0 as the "last" value, the verification fails:

secret = OneTimePassEcto.Base.gen_secret(32)
code = OneTimePassEcto.Base.gen_hotp(secret, 0)
OneTimePassEcto.Base.check_hotp(code, secret) # => false

But it works with 1:

secret = OneTimePassEcto.Base.gen_secret(32)
code = OneTimePassEcto.Base.gen_hotp(secret, 1)
OneTimePassEcto.Base.check_hotp(code, secret) # => 1

Took me a while to figure this out! Seems like it would be good to document this somewhere?

check_totp/3 always returns false

I think there may be a bug with check_totp/3. The call to check_token/4 always returns false and I'm not sure why. It appears that the first case clause in check_token/4 never gets hit. Is there a reason why this is happening?

Here is the part of the library code that I believe is the culprit.

  def check_totp(token, secret, opts \\ []) do
    valid_token(token, Keyword.get(opts, :token_length, 6)) and
      (
        {count, window} =
          {
            Keyword.get(opts, :interval_length, 30) |> interval_count,
            Keyword.get(opts, :window, 1)
          }

        check_token(token, secret, count - window, count + window, opts)
      )
  end

  defp interval_count(interval_length) do
    trunc(System.system_time(:second) / interval_length)
  end

  defp check_token(_token, _secret, current, last, _opts) when current > last do
    false
  end

  defp check_token(token, secret, current, last, opts) do
    case gen_hotp(secret, current, opts) do
      ^token ->  
         # never enters this clause
         current
      _ -> 
        # current will keep increasing until it is greater than last, 
        # which will lead to the preceding function clause always returning false
        check_token(token, secret, current + 1, last, opts) 
    end

Incompatibility with Erlang/OTP 24

Erlang/OTP 24 dropped :crypto.hmac.

(UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead

One must use :crypto.mac/4 which is OTP 22+

It's not super clear to me how to handle Erlang as a dependency; I'd propose a PR but I'm not sure how to not break compatiblity with OTP < 22.


  1) test check totp with default options (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:62
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/one_time_pass_ecto_test.exs:63: (test)



  2) test check hotp using different secret blocks access using original hotp secret (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:45
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: login(user, otp_secret: :second_otp_secret, otp_last: :second_otp_last)
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 0, 0, 0, 1>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
       (ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
       test/one_time_pass_ecto_test.exs:48: (test)



  3) test check totp with default options and alternate secret (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:69
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: token = OneTimePassEcto.Base.gen_totp("E4IX6ABMKZX7GN56")
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 3, 56, 206, 215>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/one_time_pass_ecto_test.exs:70: (test)



  4) test check hotp with default options (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:24
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: {:ok, %{id: id, otp_last: otp_last}} = login(user, [])
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
       (ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
       test/one_time_pass_ecto_test.exs:26: (test)



  5) test disallow totp check with same token (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:76
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/one_time_pass_ecto_test.exs:77: (test)



  6) test disallow totp check with earlier token that is still valid (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:85
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/one_time_pass_ecto_test.exs:86: (test)



  7) test check hotp with updated last (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:35
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: {:ok, %{id: id, otp_last: otp_last}} = login(user, [])
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 19>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
       (ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
       test/one_time_pass_ecto_test.exs:37: (test)



  8) test check hotp using alternate secret (OneTimePassEctoTest)
     test/one_time_pass_ecto_test.exs:51
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: {:ok, %{id: id, second_otp_last: otp_last}} = login(user, opts)
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 0, 0, 0, 1>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
       (ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection 2.0warning: Supervisor.terminate_child/2 with a PID is deprecated, please use DynamicSupervisor instead
  (elixir 1.12.0) lib/supervisor.ex:857: Supervisor.terminate_child/2
  (db_connection 2.0.3) lib/db_connection/watcher.ex:38: DBConnection.Watcher.handle_info/2
  (stdlib 3.15) gen_server.erl:695: :gen_server.try_dispatch/4
  (stdlib 3.15) gen_server.erl:771: :gen_server.handle_msg/6
  (stdlib 3.15) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
       test/one_time_pass_ecto_test.exs:54: (test)



  9) test check hotp (OneTimePassEcto.BaseTest)
     test/base_test.exs:44
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: assert Base.check_hotp("816065", "MFRGGZDFMZTWQ2LK") == 2
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       test/base_test.exs:45: (test)



 10) test generate hotp (OneTimePassEcto.BaseTest)
     test/base_test.exs:33
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: assert Base.gen_hotp("MFRGGZDFMZTWQ2LK", 1) == "765705"
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/base_test.exs:34: (test)



 11) test check hotp fails for outside window (OneTimePassEcto.BaseTest)
     test/base_test.exs:50
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: refute Base.check_hotp("088239", "MFRGGZDFMZTWQ2LK", last: 10)
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 11>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
       test/base_test.exs:51: (test)

..

 12) test check totp (OneTimePassEcto.BaseTest)
     test/base_test.exs:56
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: assert Base.gen_totp("MFRGGZDFMZTWQ2LK") |> Base.check_totp("MFRGGZDFMZTWQ2LK")
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/base_test.exs:57: (test)



 13) test check totp fails for outside window (OneTimePassEcto.BaseTest)
     test/base_test.exs:63
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: token = Base.gen_hotp("MFRGGZDFMZTWQ2LK", get_count() - 2)
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 213>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/base_test.exs:64: (test)

.

 14) test generate hotp with zero padding (OneTimePassEcto.BaseTest)
     test/base_test.exs:40
     ** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
     code: assert Base.gen_hotp("MFRGGZDFMZTWQ2LK", 19) == "088239"
     stacktrace:
       (crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 19>>)
       (one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
       test/base_test.exs:41: (test)



Finished in 0.1 seconds (0.00s async, 0.1s sync)
17 tests, 14 failures

Randomized with seed 669903

Warning when using the OneTimePassEcto.Base.gen_totp with Elixir 1.8

The iex shell shows the following warning when using the library with Elixir 1.8 compiled using OTP 21.

iex> OneTimePassEcto.Base.gen_totp(s, [{:token_length, 4}, {:interval_length, 300}])
warning: deprecated time unit: :seconds. A time unit should be :second, :millisecond, 
:microsecond, :nanosecond, or a positive integer
  (one_time_pass_ecto) lib/one_time_pass_ecto/base.ex:147: OneTimePassEcto.Base.interval_count/1
  (one_time_pass_ecto) lib/one_time_pass_ecto/base.ex:97: OneTimePassEcto.Base.gen_totp/2
  (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
  (elixir) src/elixir.erl:258: :elixir.eval_forms/4
  (iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5
  (iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3
  (iex) lib/iex/evaluator.ex:215: IEx.Evaluator.eval/3
  (iex) lib/iex/evaluator.ex:103: IEx.Evaluator.loop/1
  (iex) lib/iex/evaluator.ex:27: IEx.Evaluator.init/4
  (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
"8613"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.