From b9f59ca2986075112ff1de42320168affa10921e Mon Sep 17 00:00:00 2001 From: Jason David Davies Date: Tue, 12 Jan 2010 19:29:23 +0000 Subject: Add utility for verifying hashes. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@898477 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_httpd_auth.erl | 32 +++++++++++++++++++------------- src/couchdb/couch_util.erl | 17 +++++++++++++++++ src/erlang-oauth/oauth_hmac_sha1.erl | 2 +- src/erlang-oauth/oauth_plaintext.erl | 2 +- test/etap/040-util.t | 14 +++++++++++++- 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/couchdb/couch_httpd_auth.erl b/src/couchdb/couch_httpd_auth.erl index a1222df4..5c87f427 100644 --- a/src/couchdb/couch_httpd_auth.erl +++ b/src/couchdb/couch_httpd_auth.erl @@ -71,8 +71,9 @@ default_authentication_handler(Req) -> UserProps -> UserSalt = proplists:get_value(<<"salt">>, UserProps, <<>>), PasswordHash = hash_password(?l2b(Pass), UserSalt), - case proplists:get_value(<<"password_sha">>, UserProps, nil) of - ExpectedHash when ExpectedHash == PasswordHash -> + ExpectedHash = proplists:get_value(<<"password_sha">>, UserProps, nil), + case couch_util:verify(ExpectedHash, PasswordHash) of + true -> Req#httpd{user_ctx=#user_ctx{ name=?l2b(User), roles=proplists:get_value(<<"roles">>, UserProps, []), @@ -268,15 +269,19 @@ cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) -> Timeout = to_int(couch_config:get("couch_httpd_auth", "timeout", 600)), ?LOG_DEBUG("timeout ~p", [Timeout]), case (catch erlang:list_to_integer(TimeStr, 16)) of - TimeStamp when CurrentTime < TimeStamp + Timeout - andalso ExpectedHash == Hash -> - TimeLeft = TimeStamp + Timeout - CurrentTime, - ?LOG_DEBUG("Successful cookie auth as: ~p", [User]), - Req#httpd{user_ctx=#user_ctx{ - name=?l2b(User), - roles=proplists:get_value(<<"roles">>, UserProps, []), - user_doc=proplists:get_value(<<"user_doc">>, UserProps, null) - }, auth={FullSecret, TimeLeft < Timeout*0.9}}; + TimeStamp when CurrentTime < TimeStamp + Timeout -> + case couch_util:verify(ExpectedHash, Hash) of + true -> + TimeLeft = TimeStamp + Timeout - CurrentTime, + ?LOG_DEBUG("Successful cookie auth as: ~p", [User]), + Req#httpd{user_ctx=#user_ctx{ + name=?l2b(User), + roles=proplists:get_value(<<"roles">>, UserProps, []), + user_doc=proplists:get_value(<<"user_doc">>, UserProps, null) + }, auth={FullSecret, TimeLeft < Timeout*0.9}}; + _Else -> + Req + end; _Else -> Req end @@ -344,8 +349,9 @@ handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req) -> end, UserSalt = proplists:get_value(<<"salt">>, User, <<>>), PasswordHash = hash_password(Password, UserSalt), - case proplists:get_value(<<"password_sha">>, User, nil) of - ExpectedHash when ExpectedHash == PasswordHash -> + ExpectedHash = proplists:get_value(<<"password_sha">>, User, nil), + case couch_util:verify(ExpectedHash, PasswordHash) of + true -> % setup the session cookie Secret = ?l2b(ensure_cookie_auth_secret()), {NowMS, NowS, _} = erlang:now(), diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl index 6edfb781..45d1d336 100644 --- a/src/couchdb/couch_util.erl +++ b/src/couchdb/couch_util.erl @@ -21,6 +21,7 @@ -export([file_read_size/1, get_nested_json_value/2, json_user_ctx/1]). -export([to_binary/1, to_integer/1, to_list/1, url_encode/1]). -export([json_encode/1, json_decode/1]). +-export([verify/2]). -include("couch_db.hrl"). -include_lib("kernel/include/file.hrl"). @@ -423,3 +424,19 @@ json_decode(V) -> _Type:_Error -> throw({invalid_json,V}) end. + +verify([X|RestX], [Y|RestY], Result) -> + verify(RestX, RestY, (X bxor Y) bor Result); +verify([], [], Result) -> + Result == 0. + +verify(<>, <>) -> + verify(?b2l(X), ?b2l(Y)); +verify(X, Y) when is_list(X) and is_list(Y) -> + case length(X) == length(Y) of + true -> + verify(X, Y, 0); + false -> + false + end; +verify(_X, _Y) -> false. diff --git a/src/erlang-oauth/oauth_hmac_sha1.erl b/src/erlang-oauth/oauth_hmac_sha1.erl index 69064edd..79d59f37 100644 --- a/src/erlang-oauth/oauth_hmac_sha1.erl +++ b/src/erlang-oauth/oauth_hmac_sha1.erl @@ -8,4 +8,4 @@ signature(BaseString, CS, TS) -> base64:encode_to_string(crypto:sha_mac(Key, BaseString)). verify(Signature, BaseString, CS, TS) -> - Signature =:= signature(BaseString, CS, TS). + couch_util:verify(signature(BaseString, CS, TS), Signature). diff --git a/src/erlang-oauth/oauth_plaintext.erl b/src/erlang-oauth/oauth_plaintext.erl index d8085e02..41a1e9b2 100644 --- a/src/erlang-oauth/oauth_plaintext.erl +++ b/src/erlang-oauth/oauth_plaintext.erl @@ -7,4 +7,4 @@ signature(CS, TS) -> oauth_uri:calate("&", [CS, TS]). verify(Signature, CS, TS) -> - Signature =:= signature(CS, TS). + couch_util:verify(signature(CS, TS), Signature). diff --git a/test/etap/040-util.t b/test/etap/040-util.t index 6d6da2c1..4500d38a 100755 --- a/test/etap/040-util.t +++ b/test/etap/040-util.t @@ -17,7 +17,7 @@ main(_) -> test_util:init_code_path(), application:start(crypto), - etap:plan(11), + etap:plan(16), case (catch test()) of ok -> etap:end_tests(); @@ -88,4 +88,16 @@ test() -> etap:ok(not couch_util:should_flush(), "Checking to flush invokes GC."), + % verify + etap:is(true, couch_util:verify("It4Vooya", "It4Vooya"), + "String comparison."), + etap:is(false, couch_util:verify("It4VooyaX", "It4Vooya"), + "String comparison (unequal lengths)."), + etap:is(true, couch_util:verify(<<"ahBase3r">>, <<"ahBase3r">>), + "Binary comparison."), + etap:is(false, couch_util:verify(<<"ahBase3rX">>, <<"ahBase3r">>), + "Binary comparison (unequal lengths)."), + etap:is(false, couch_util:verify(nil, <<"ahBase3r">>), + "Binary comparison with atom."), + ok. -- cgit v1.2.3