You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2014/10/29 22:49:37 UTC
couch commit: updated refs/heads/two-factor-auth to fbae1ad
Repository: couchdb-couch
Updated Branches:
refs/heads/two-factor-auth cfb5718e2 -> fbae1ada4 (forced update)
Implement two factor authentication
If enabled, require a second factor to acquire a session cookie and
reject basic authentication attempts (as second factor cannot be
presented). Allow previous and next token for clock skew.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/fbae1ada
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/fbae1ada
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/fbae1ada
Branch: refs/heads/two-factor-auth
Commit: fbae1ada412f806503defa455dc7d1d9d060a877
Parents: 40c5c85
Author: Robert Newson <rn...@apache.org>
Authored: Wed Oct 29 17:39:21 2014 +0000
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Oct 29 21:48:30 2014 +0000
----------------------------------------------------------------------
src/couch_hotp.erl | 33 +++++++++++++++++++++++++++++++
src/couch_httpd_auth.erl | 44 ++++++++++++++++++++++++++++++++++++++++++
src/couch_totp.erl | 23 ++++++++++++++++++++++
test/couch_hotp_tests.erl | 28 +++++++++++++++++++++++++++
test/couch_totp_tests.erl | 24 +++++++++++++++++++++++
5 files changed, 152 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fbae1ada/src/couch_hotp.erl
----------------------------------------------------------------------
diff --git a/src/couch_hotp.erl b/src/couch_hotp.erl
new file mode 100644
index 0000000..10ccc33
--- /dev/null
+++ b/src/couch_hotp.erl
@@ -0,0 +1,33 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_hotp).
+
+-export([generate/2, generate/3]).
+
+generate(Key, Counter) when is_binary(Key), is_integer(Counter) ->
+ generate(Key, Counter, 6).
+
+generate(Key, Counter, OutputLen)
+ when is_binary(Key), is_integer(Counter), is_integer(OutputLen) ->
+ Hmac = crypto:sha_mac(Key, <<Counter:64>>),
+ Offset = binary:last(Hmac) band 16#f,
+ Code =
+ ((binary:at(Hmac, Offset) band 16#7f) bsl 24) +
+ ((binary:at(Hmac, Offset + 1) band 16#ff) bsl 16) +
+ ((binary:at(Hmac, Offset + 2) band 16#ff) bsl 8) +
+ ((binary:at(Hmac, Offset + 3) band 16#ff)),
+ case OutputLen of
+ 6 -> Code rem 1000000;
+ 7 -> Code rem 10000000;
+ 8 -> Code rem 100000000
+ end.
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fbae1ada/src/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl
index 7c55a2b..8c91252 100644
--- a/src/couch_httpd_auth.erl
+++ b/src/couch_httpd_auth.erl
@@ -75,6 +75,7 @@ default_authentication_handler(Req, AuthModule) ->
nil ->
throw({unauthorized, <<"Name or password is incorrect.">>});
UserProps ->
+ reject_if_two_factor(UserProps),
UserName = ?l2b(User),
Password = ?l2b(Pass),
case authenticate(Password, UserProps) of
@@ -287,6 +288,7 @@ handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req, AuthModule) ->
end,
case authenticate(Password, UserProps) of
true ->
+ verify_two_factor(UserProps, Form),
UserProps2 = maybe_upgrade_password_hash(UserName, Password, UserProps, AuthModule),
% setup the session cookie
Secret = ?l2b(ensure_cookie_auth_secret()),
@@ -430,3 +432,45 @@ max_age() ->
config:get("couch_httpd_auth", "timeout", "600")),
[{max_age, Timeout}]
end.
+
+reject_if_two_factor(User) ->
+ case get_two_factor_config(User) of
+ undefined ->
+ ok;
+ _ ->
+ throw({unauthorized, <<"Name or password is incorrect.">>})
+ end.
+
+verify_two_factor(User, Form) ->
+ case get_two_factor_config(User) of
+ undefined ->
+ ok;
+ {Props} ->
+ Key = couch_util:get_value(<<"key">>, Props),
+ Token = ?l2b(couch_util:get_value("token", Form, "")),
+ verify_token(Key, Token)
+ end.
+
+get_two_factor_config(User) ->
+ couch_util:get_value(<<"2f">>, User).
+
+verify_token(Key, Token) ->
+ Now = make_cookie_time(),
+ Tokens = [generate_token(Key, Now - 30),
+ generate_token(Key, Now),
+ generate_token(Key, Now + 30)],
+ %% evaluate all tokens in constant time
+ Match = lists:foldl(fun(T, Acc) -> couch_util:verify(T, Token) or Acc end,
+ false, Tokens),
+ case Match of
+ true ->
+ ok;
+ _ ->
+ throw({unauthorized, <<"Name or password is incorrect.">>})
+ end.
+
+generate_token(Key, Timestamp) ->
+ integer_to_binary(couch_totp:generate(Key, Timestamp, 30, 6)).
+
+integer_to_binary(Int) when is_integer(Int) ->
+ ?l2b(integer_to_list(Int)).
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fbae1ada/src/couch_totp.erl
----------------------------------------------------------------------
diff --git a/src/couch_totp.erl b/src/couch_totp.erl
new file mode 100644
index 0000000..c3a6a7e
--- /dev/null
+++ b/src/couch_totp.erl
@@ -0,0 +1,23 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_totp).
+
+-export([generate/3, generate/4]).
+
+generate(Key, CounterSecs, StepSecs) ->
+ generate(Key, CounterSecs, StepSecs, 8).
+
+generate(Key, CounterSecs, StepSecs, OutputLen)
+ when is_binary(Key), is_integer(CounterSecs), is_integer(StepSecs),
+ is_integer(OutputLen) ->
+ couch_hotp:generate(Key, CounterSecs div StepSecs, OutputLen).
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fbae1ada/test/couch_hotp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_hotp_tests.erl b/test/couch_hotp_tests.erl
new file mode 100644
index 0000000..87d2dff
--- /dev/null
+++ b/test/couch_hotp_tests.erl
@@ -0,0 +1,28 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_hotp_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+test_vector_test() ->
+ Key = <<"12345678901234567890">>,
+ ?assertEqual(755224, couch_hotp:generate(Key, 0)),
+ ?assertEqual(287082, couch_hotp:generate(Key, 1)),
+ ?assertEqual(359152, couch_hotp:generate(Key, 2)),
+ ?assertEqual(969429, couch_hotp:generate(Key, 3)),
+ ?assertEqual(338314, couch_hotp:generate(Key, 4)),
+ ?assertEqual(254676, couch_hotp:generate(Key, 5)),
+ ?assertEqual(287922, couch_hotp:generate(Key, 6)),
+ ?assertEqual(162583, couch_hotp:generate(Key, 7)),
+ ?assertEqual(399871, couch_hotp:generate(Key, 8)),
+ ?assertEqual(520489, couch_hotp:generate(Key, 9)).
http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fbae1ada/test/couch_totp_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_totp_tests.erl b/test/couch_totp_tests.erl
new file mode 100644
index 0000000..33596ed
--- /dev/null
+++ b/test/couch_totp_tests.erl
@@ -0,0 +1,24 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_totp_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+test_vector_test() ->
+ Key = <<"12345678901234567890">>,
+ ?assertEqual(94287082, couch_totp:generate(Key, 59, 30)),
+ ?assertEqual(07081804, couch_totp:generate(Key, 1111111109, 30)),
+ ?assertEqual(14050471, couch_totp:generate(Key, 1111111111, 30)),
+ ?assertEqual(89005924, couch_totp:generate(Key, 1234567890, 30)),
+ ?assertEqual(69279037, couch_totp:generate(Key, 2000000000, 30)),
+ ?assertEqual(65353130, couch_totp:generate(Key, 20000000000, 30)).