You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2012/12/06 13:15:23 UTC

git commit: update erlang-oauth to 1.3.0

Updated Branches:
  refs/heads/1597-update-erlang-oauth-1-3-0 ba4cab37b -> d74e93c90 (forced update)


update erlang-oauth to 1.3.0


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/d74e93c9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/d74e93c9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/d74e93c9

Branch: refs/heads/1597-update-erlang-oauth-1-3-0
Commit: d74e93c904fc1b55a2f83bb6e7362e602cd72b16
Parents: b9af7ea
Author: Jan Lehnardt <ja...@apache.org>
Authored: Mon Nov 12 21:11:39 2012 +0100
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Thu Dec 6 13:15:08 2012 +0100

----------------------------------------------------------------------
 LICENSE                                            |    2 +-
 NOTICE                                             |    4 +-
 .../src/couch_replicator_httpc.erl                 |    4 +-
 src/couchdb/couch_httpd_oauth.erl                  |    2 +-
 src/erlang-oauth/Makefile.am                       |   15 +-
 src/erlang-oauth/oauth.erl                         |  328 ++++++++++++---
 src/erlang-oauth/oauth_hmac_sha1.erl               |   11 -
 src/erlang-oauth/oauth_http.erl                    |   22 -
 src/erlang-oauth/oauth_plaintext.erl               |   10 -
 src/erlang-oauth/oauth_rsa_sha1.erl                |   30 --
 src/erlang-oauth/oauth_unix.erl                    |   16 -
 src/erlang-oauth/oauth_uri.erl                     |  100 -----
 test/etap/160-vhosts.t                             |    4 +-
 13 files changed, 278 insertions(+), 270 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index be511b1..f65c452 100644
--- a/LICENSE
+++ b/LICENSE
@@ -350,7 +350,7 @@ For the src/ibrowse component:
 
 For the src/erlang-oauth component:
 
-  Copyright (c) 2008-2009 Tim Fletcher <http://tfletcher.com/>
+  Copyright the authors and contributors. All rights reserved.
 
   Permission is hereby granted, free of charge, to any person
   obtaining a copy of this software and associated documentation

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/NOTICE
----------------------------------------------------------------------
diff --git a/NOTICE b/NOTICE
index ef3ff9e..6608656 100644
--- a/NOTICE
+++ b/NOTICE
@@ -38,9 +38,9 @@ This product also includes the following third-party components:
 
    Copyright 2009, Chandrashekhar Mullaparthi
 
- * Erlang OAuth (http://github.com/tim/erlang-oauth/tree/master)
+ * Erlang OAuth (http://github.com/tim/erlang-oauth)
 
-   Copyright 2009, Tim Fletcher <http://tfletcher.com/>
+   Copyright 2012, the authors and contributors
 
  * ETap (http://github.com/ngerakines/etap/)
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/couch_replicator/src/couch_replicator_httpc.erl
----------------------------------------------------------------------
diff --git a/src/couch_replicator/src/couch_replicator_httpc.erl b/src/couch_replicator/src/couch_replicator_httpc.erl
index 8773383..c6cf321 100644
--- a/src/couch_replicator/src/couch_replicator_httpc.erl
+++ b/src/couch_replicator/src/couch_replicator_httpc.erl
@@ -244,11 +244,11 @@ oauth_header(#httpdb{url = BaseUrl, oauth = OAuth}, ConnParams) ->
     head -> "HEAD"
     end,
     QSL = get_value(qs, ConnParams, []),
-    OAuthParams = oauth:signed_params(Method,
+    OAuthParams = oauth:sign(Method,
         BaseUrl ++ get_value(path, ConnParams, []),
         QSL, Consumer, OAuth#oauth.token, OAuth#oauth.token_secret) -- QSL,
     [{"Authorization",
-        "OAuth " ++ oauth_uri:params_to_header_string(OAuthParams)}].
+        "OAuth " ++ oauth:header_params_encode(OAuthParams)}].
 
 
 do_redirect(Worker, Code, Headers, #httpdb{url = Url} = HttpDb, Params, Cb) ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/couchdb/couch_httpd_oauth.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_oauth.erl b/src/couchdb/couch_httpd_oauth.erl
index 90990e6..2094c08 100644
--- a/src/couchdb/couch_httpd_oauth.erl
+++ b/src/couchdb/couch_httpd_oauth.erl
@@ -189,7 +189,7 @@ serve_oauth(#httpd{mochi_req=MochiReq}=Req, Fun, FailSilently) ->
                 _ -> ""
             end
     end,
-    HeaderParams = oauth_uri:params_from_header_string(AuthHeader),
+    HeaderParams = oauth:header_params_decode(AuthHeader),
     %Realm = couch_util:get_value("realm", HeaderParams),
 
     % get requested path

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/Makefile.am
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/Makefile.am b/src/erlang-oauth/Makefile.am
index 48b7648..a334677 100644
--- a/src/erlang-oauth/Makefile.am
+++ b/src/erlang-oauth/Makefile.am
@@ -14,25 +14,14 @@ oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin
 
 oauth_file_collection = \
 	oauth.app.in \
-    oauth.erl \
-    oauth_hmac_sha1.erl \
-    oauth_http.erl \
-    oauth_plaintext.erl \
-    oauth_rsa_sha1.erl \
-    oauth_unix.erl \
-    oauth_uri.erl
+    oauth.erl
 
 # Removed oauth_rsa_sha1.beam until we require R12B5 or
 # we add a ./configure option to enable it.
 
 oauthebin_make_generated_file_list = \
 	oauth.app \
-    oauth.beam \
-    oauth_hmac_sha1.beam \
-    oauth_http.beam \
-    oauth_plaintext.beam \
-    oauth_unix.beam \
-    oauth_uri.beam
+    oauth.beam
 
 oauthebin_DATA = \
 	$(oauthebin_make_generated_file_list)

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth.erl b/src/erlang-oauth/oauth.erl
index 866655c..e75d5fd 100644
--- a/src/erlang-oauth/oauth.erl
+++ b/src/erlang-oauth/oauth.erl
@@ -1,34 +1,54 @@
 -module(oauth).
 
--export(
-  [ get/5
-  , header/1
-  , post/5
-  , signature/5
-  , signature_base_string/3
-  , signed_params/6
-  , token/1
-  , token_secret/1
-  , uri/2
-  , verify/6
-  ]).
+-export([get/3, get/5, get/6, post/3, post/5, post/6, put/6, put/7, uri/2, header/1,
+  sign/6, params_decode/1, token/1, token_secret/1, verify/6]).
 
+-export([plaintext_signature/2, hmac_sha1_signature/5,
+  hmac_sha1_signature/3, rsa_sha1_signature/4, rsa_sha1_signature/2,
+  signature_base_string/3, params_encode/1]).
+
+-export([plaintext_verify/3, hmac_sha1_verify/6, hmac_sha1_verify/4,
+  rsa_sha1_verify/5, rsa_sha1_verify/3]).
+
+-export([header_params_encode/1, header_params_decode/1,
+  uri_params_encode/1, uri_params_decode/1]).
+
+-include_lib("public_key/include/public_key.hrl").
+
+get(URL, ExtraParams, Consumer) ->
+  get(URL, ExtraParams, Consumer, "", "").
 
 get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
-  SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
-  oauth_http:get(uri(URL, SignedParams)).
+  get(URL, ExtraParams, Consumer, Token, TokenSecret, []).
+
+get(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
+  SignedParams = sign("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
+  http_request(get, {uri(URL, SignedParams), []}, HttpcOptions).
+
+post(URL, ExtraParams, Consumer) ->
+  post(URL, ExtraParams, Consumer, "", "").
 
 post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
-  SignedParams = signed_params("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
-  oauth_http:post(URL, oauth_uri:params_to_string(SignedParams)).
+  post(URL, ExtraParams, Consumer, Token, TokenSecret, []).
+
+post(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
+  SignedParams = sign("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
+  http_request(post, {URL, [], "application/x-www-form-urlencoded", uri_params_encode(SignedParams)}, HttpcOptions).
+
+put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret) ->
+  put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, []).
+
+put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, HttpcOptions) ->
+  SignedParams = sign("PUT", URL, ExtraParams, Consumer, Token, TokenSecret),
+  http_request(put, {uri(URL, SignedParams), [], ContentType, Body}, HttpcOptions).
 
 uri(Base, []) ->
   Base;
 uri(Base, Params) ->
-  lists:concat([Base, "?", oauth_uri:params_to_string(Params)]).
+  lists:concat([Base, "?", uri_params_encode(Params)]).
 
 header(Params) ->
-  {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.
+  {"Authorization", "OAuth " ++ header_params_encode(Params)}.
 
 token(Params) ->
   proplists:get_value("oauth_token", Params).
@@ -36,49 +56,28 @@ token(Params) ->
 token_secret(Params) ->
   proplists:get_value("oauth_token_secret", Params).
 
-verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
-  case signature_method(Consumer) of
-    plaintext ->
-      oauth_plaintext:verify(Signature, consumer_secret(Consumer), TokenSecret);
-    hmac_sha1 ->
-      BaseString = signature_base_string(HttpMethod, URL, Params),
-      oauth_hmac_sha1:verify(Signature, BaseString, consumer_secret(Consumer), TokenSecret);
-    rsa_sha1 ->
-      BaseString = signature_base_string(HttpMethod, URL, Params),
-      oauth_rsa_sha1:verify(Signature, BaseString, consumer_secret(Consumer))
-  end.
+consumer_key(_Consumer={Key, _, _}) ->
+  Key.
 
-signed_params(HttpMethod, URL, ExtraParams, Consumer, Token, TokenSecret) ->
-  Params = token_param(Token, params(Consumer, ExtraParams)),
-  [{"oauth_signature", signature(HttpMethod, URL, Params, Consumer, TokenSecret)}|Params].
+consumer_secret(_Consumer={_, Secret, _}) ->
+  Secret.
 
-signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
-  case signature_method(Consumer) of
-    plaintext ->
-      oauth_plaintext:signature(consumer_secret(Consumer), TokenSecret);
-    hmac_sha1 ->
-      BaseString = signature_base_string(HttpMethod, URL, Params),
-      oauth_hmac_sha1:signature(BaseString, consumer_secret(Consumer), TokenSecret);
-    rsa_sha1 ->
-      BaseString = signature_base_string(HttpMethod, URL, Params),
-      oauth_rsa_sha1:signature(BaseString, consumer_secret(Consumer))
-  end.
+signature_method(_Consumer={_, _, Method}) ->
+  Method.
 
-signature_base_string(HttpMethod, URL, Params) ->
-  NormalizedURL = oauth_uri:normalize(URL),
-  NormalizedParams = oauth_uri:params_to_string(lists:sort(Params)),
-  oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]).
+sign(HttpMethod, URL, Params, Consumer, Token, TokenSecret) ->
+  SignatureParams = signature_params(Consumer, Params, Token),
+  Signature = signature(HttpMethod, URL, SignatureParams, Consumer, TokenSecret),
+  [{"oauth_signature", Signature} | SignatureParams].
 
-token_param("", Params) ->
-  Params;
-token_param(Token, Params) ->
-  [{"oauth_token", Token}|Params].
+signature_params(Consumer, Params, "") ->
+  signature_params(Consumer, Params);
+signature_params(Consumer, Params, Token) ->
+  signature_params(Consumer, [{"oauth_token", Token} | Params]).
 
-params(Consumer, Params) ->
+signature_params(Consumer, Params) ->
+  Timestamp = unix_timestamp(),
   Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
-  params(Consumer, oauth_unix:timestamp(), Nonce, Params).
-
-params(Consumer, Timestamp, Nonce, Params) ->
   [ {"oauth_version", "1.0"}
   , {"oauth_nonce", Nonce}
   , {"oauth_timestamp", integer_to_list(Timestamp)}
@@ -87,6 +86,26 @@ params(Consumer, Timestamp, Nonce, Params) ->
   | Params
   ].
 
+verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
+  case signature_method(Consumer) of
+    plaintext ->
+      plaintext_verify(Signature, Consumer, TokenSecret);
+    hmac_sha1 ->
+      hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret);
+    rsa_sha1 ->
+      rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer)
+  end.
+
+signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
+  case signature_method(Consumer) of
+    plaintext ->
+      plaintext_signature(Consumer, TokenSecret);
+    hmac_sha1 ->
+      hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret);
+    rsa_sha1 ->
+      rsa_sha1_signature(HttpMethod, URL, Params, Consumer)
+  end.
+
 signature_method_string(Consumer) ->
   case signature_method(Consumer) of
     plaintext ->
@@ -97,11 +116,200 @@ signature_method_string(Consumer) ->
       "RSA-SHA1"
   end.
 
-signature_method(_Consumer={_, _, Method}) ->
-  Method.
+plaintext_signature(Consumer, TokenSecret) ->
+  uri_join([consumer_secret(Consumer), TokenSecret]).
 
-consumer_secret(_Consumer={_, Secret, _}) ->
-  Secret.
+plaintext_verify(Signature, Consumer, TokenSecret) ->
+  verify_in_constant_time(Signature, plaintext_signature(Consumer, TokenSecret)).
 
-consumer_key(_Consumer={Key, _, _}) ->
+hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
+  BaseString = signature_base_string(HttpMethod, URL, Params),
+  hmac_sha1_signature(BaseString, Consumer, TokenSecret).
+
+hmac_sha1_signature(BaseString, Consumer, TokenSecret) ->
+  Key = uri_join([consumer_secret(Consumer), TokenSecret]),
+  base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
+
+hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
+  verify_in_constant_time(Signature, hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret)).
+
+hmac_sha1_verify(Signature, BaseString, Consumer, TokenSecret) ->
+  verify_in_constant_time(Signature, hmac_sha1_signature(BaseString, Consumer, TokenSecret)).
+
+rsa_sha1_signature(HttpMethod, URL, Params, Consumer) ->
+  BaseString = signature_base_string(HttpMethod, URL, Params),
+  rsa_sha1_signature(BaseString, Consumer).
+
+rsa_sha1_signature(BaseString, Consumer) ->
+  Key = read_private_key(consumer_secret(Consumer)),
+  base64:encode_to_string(public_key:sign(list_to_binary(BaseString), sha, Key)).
+
+rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer) ->
+  BaseString = signature_base_string(HttpMethod, URL, Params),
+  rsa_sha1_verify(Signature, BaseString, Consumer).
+
+rsa_sha1_verify(Signature, BaseString, Consumer) ->
+  Key = read_cert_key(consumer_secret(Consumer)),
+  public_key:verify(to_binary(BaseString), sha, base64:decode(Signature), Key).
+
+verify_in_constant_time(<<X/binary>>, <<Y/binary>>) ->
+  verify_in_constant_time(binary_to_list(X), binary_to_list(Y));
+verify_in_constant_time(X, Y) when is_list(X) and is_list(Y) ->
+  case length(X) == length(Y) of
+    true ->
+      verify_in_constant_time(X, Y, 0);
+    false ->
+      false
+  end.
+
+verify_in_constant_time([X | RestX], [Y | RestY], Result) ->
+  verify_in_constant_time(RestX, RestY, (X bxor Y) bor Result);
+verify_in_constant_time([], [], Result) ->
+  Result == 0.
+
+signature_base_string(HttpMethod, URL, Params) ->
+  uri_join([HttpMethod, uri_normalize(URL), params_encode(Params)]).
+
+params_encode(Params) ->
+  % cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
+  Encoded = [{uri_encode(K), uri_encode(V)} || {K, V} <- Params],
+  Sorted = lists:sort(Encoded),
+  Concatenated = [lists:concat([K, "=", V]) || {K, V} <- Sorted],
+  string:join(Concatenated, "&").
+
+params_decode(_Response={{_, _, _}, _, Body}) ->
+  uri_params_decode(Body).
+
+http_request(Method, Request, Options) ->
+  httpc:request(Method, Request, [{autoredirect, false}], Options).
+
+unix_timestamp() ->
+  unix_timestamp(calendar:universal_time()).
+
+unix_timestamp(DateTime) ->
+  unix_seconds(DateTime) - unix_epoch().
+
+unix_epoch() ->
+  unix_seconds({{1970,1,1},{00,00,00}}).
+
+unix_seconds(DateTime) ->
+  calendar:datetime_to_gregorian_seconds(DateTime).
+
+read_cert_key(Path) when is_list(Path) ->
+  {ok, Contents} = file:read_file(Path),
+  [{'Certificate', DerCert, not_encrypted}] = public_key:pem_decode(Contents),
+  read_cert_key(public_key:pkix_decode_cert(DerCert, otp));
+read_cert_key(#'OTPCertificate'{tbsCertificate=Cert}) ->
+  read_cert_key(Cert);
+read_cert_key(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
+  read_cert_key(Info);
+read_cert_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
   Key.
+
+read_private_key(Path) ->
+  {ok, Contents} = file:read_file(Path),
+  [Info] = public_key:pem_decode(Contents),
+  public_key:pem_entry_decode(Info).
+
+to_binary(Term) when is_list(Term) ->
+  list_to_binary(Term);
+to_binary(Term) when is_binary(Term) ->
+  Term.
+
+header_params_encode(Params) ->
+  intercalate(", ", [lists:concat([uri_encode(K), "=\"", uri_encode(V), "\""]) || {K, V} <- Params]).
+
+header_params_decode(String) ->
+  [header_param_decode(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
+
+header_param_decode(Param) ->
+  [Key, QuotedValue] = string:tokens(Param, "="),
+  Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
+  {uri_decode(Key), uri_decode(Value)}.
+
+uri_normalize(URI) ->
+  case http_uri:parse(URI) of
+    {ok, {Scheme, UserInfo, Host, Port, Path, _Query}} -> % R15B
+      uri_normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
+    {Scheme, UserInfo, Host, Port, Path, _Query} ->
+      uri_normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
+    Else ->
+      Else
+  end.
+
+uri_normalize(http, UserInfo, Host, 80, Acc) ->
+  uri_normalize(http, UserInfo, [Host|Acc]);
+uri_normalize(https, UserInfo, Host, 443, Acc) ->
+  uri_normalize(https, UserInfo, [Host|Acc]);
+uri_normalize(Scheme, UserInfo, Host, Port, Acc) ->
+  uri_normalize(Scheme, UserInfo, [Host, ":", Port|Acc]).
+
+uri_normalize(Scheme, [], Acc) ->
+  lists:concat([Scheme, "://" | Acc]);
+uri_normalize(Scheme, UserInfo, Acc) ->
+  lists:concat([Scheme, "://", UserInfo, "@" | Acc]).
+
+uri_params_encode(Params) ->
+  intercalate("&", [uri_join([K, V], "=") || {K, V} <- Params]).
+
+uri_params_decode(String) ->
+  [uri_param_decode(Substring) || Substring <- string:tokens(String, "&")].
+
+uri_param_decode(String) ->
+  [Key, Value] = string:tokens(String, "="),
+  {uri_decode(Key), uri_decode(Value)}.
+
+uri_join(Values) ->
+  uri_join(Values, "&").
+
+uri_join(Values, Separator) ->
+  string:join([uri_encode(Value) || Value <- Values], Separator).
+
+intercalate(Sep, Xs) ->
+  lists:concat(intersperse(Sep, Xs)).
+
+intersperse(_, []) ->
+  [];
+intersperse(_, [X]) ->
+  [X];
+intersperse(Sep, [X | Xs]) ->
+  [X, Sep | intersperse(Sep, Xs)].
+
+uri_encode(Term) when is_integer(Term) ->
+  integer_to_list(Term);
+uri_encode(Term) when is_atom(Term) ->
+  uri_encode(atom_to_list(Term));
+uri_encode(Term) when is_list(Term) ->
+  uri_encode(lists:reverse(Term, []), []).
+
+-define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
+
+uri_encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
+  uri_encode(T, [X | Acc]);
+uri_encode([X | T], Acc) ->
+  NewAcc = [$%, dec2hex(X bsr 4), dec2hex(X band 16#0f) | Acc],
+  uri_encode(T, NewAcc);
+uri_encode([], Acc) ->
+  Acc.
+
+uri_decode(Str) when is_list(Str) ->
+  uri_decode(Str, []).
+
+uri_decode([$%, A, B | T], Acc) ->
+  uri_decode(T, [(hex2dec(A) bsl 4) + hex2dec(B) | Acc]);
+uri_decode([X | T], Acc) ->
+  uri_decode(T, [X | Acc]);
+uri_decode([], Acc) ->
+  lists:reverse(Acc, []).
+
+-compile({inline, [{dec2hex, 1}, {hex2dec, 1}]}).
+
+dec2hex(N) when N >= 10 andalso N =< 15 ->
+  N + $A - 10;
+dec2hex(N) when N >= 0 andalso N =< 9 ->
+  N + $0.
+
+hex2dec(C) when C >= $A andalso C =< $F ->
+  C - $A + 10;
+hex2dec(C) when C >= $0 andalso C =< $9 ->
+  C - $0.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_hmac_sha1.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_hmac_sha1.erl b/src/erlang-oauth/oauth_hmac_sha1.erl
deleted file mode 100644
index 35549cf..0000000
--- a/src/erlang-oauth/oauth_hmac_sha1.erl
+++ /dev/null
@@ -1,11 +0,0 @@
--module(oauth_hmac_sha1).
-
--export([signature/3, verify/4]).
-
-
-signature(BaseString, CS, TS) ->
-  Key = oauth_uri:calate("&", [CS, TS]),
-  base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
-
-verify(Signature, BaseString, CS, TS) ->
-  couch_util:verify(Signature, signature(BaseString, CS, TS)).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_http.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_http.erl b/src/erlang-oauth/oauth_http.erl
deleted file mode 100644
index 92c806c..0000000
--- a/src/erlang-oauth/oauth_http.erl
+++ /dev/null
@@ -1,22 +0,0 @@
--module(oauth_http).
-
--export([get/1, post/2, response_params/1, response_body/1, response_code/1]).
-
-
-get(URL) ->
-  request(get, {URL, []}).
-
-post(URL, Data) ->
-  request(post, {URL, [], "application/x-www-form-urlencoded", Data}).
-
-request(Method, Request) ->
-  httpc:request(Method, Request, [{autoredirect, false}], []).
-
-response_params(Response) ->
-  oauth_uri:params_from_string(response_body(Response)).
-
-response_body({{_, _, _}, _, Body}) ->
-  Body.
-
-response_code({{_, Code, _}, _, _}) ->
-  Code.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_plaintext.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_plaintext.erl b/src/erlang-oauth/oauth_plaintext.erl
deleted file mode 100644
index 9544a0a..0000000
--- a/src/erlang-oauth/oauth_plaintext.erl
+++ /dev/null
@@ -1,10 +0,0 @@
--module(oauth_plaintext).
-
--export([signature/2, verify/3]).
-
-
-signature(CS, TS) ->
-  oauth_uri:calate("&", [CS, TS]).
-
-verify(Signature, CS, TS) ->
-  couch_util:verify(Signature, signature(CS, TS)).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_rsa_sha1.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_rsa_sha1.erl b/src/erlang-oauth/oauth_rsa_sha1.erl
deleted file mode 100644
index 6f4828e..0000000
--- a/src/erlang-oauth/oauth_rsa_sha1.erl
+++ /dev/null
@@ -1,30 +0,0 @@
--module(oauth_rsa_sha1).
-
--export([signature/2, verify/3]).
-
--include_lib("public_key/include/public_key.hrl").
-
-
-signature(BaseString, PrivateKeyPath) ->
-  {ok, [Info]} = public_key:pem_to_der(PrivateKeyPath),
-  {ok, PrivateKey} = public_key:decode_private_key(Info),
-  base64:encode_to_string(public_key:sign(list_to_binary(BaseString), PrivateKey)).
-
-verify(Signature, BaseString, PublicKey) ->
-  public_key:verify_signature(to_binary(BaseString), sha, base64:decode(Signature), public_key(PublicKey)).
-
-to_binary(Term) when is_list(Term) ->
-  list_to_binary(Term);
-to_binary(Term) when is_binary(Term) ->
-  Term.
-
-public_key(Path) when is_list(Path) ->
-  {ok, [{cert, DerCert, not_encrypted}]} = public_key:pem_to_der(Path),
-  {ok, Cert} = public_key:pkix_decode_cert(DerCert, otp),
-  public_key(Cert);
-public_key(#'OTPCertificate'{tbsCertificate=Cert}) ->
-  public_key(Cert);
-public_key(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
-  public_key(Info);
-public_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
-  Key.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_unix.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_unix.erl b/src/erlang-oauth/oauth_unix.erl
deleted file mode 100644
index 73ca314..0000000
--- a/src/erlang-oauth/oauth_unix.erl
+++ /dev/null
@@ -1,16 +0,0 @@
--module(oauth_unix).
-
--export([timestamp/0]).
-
-
-timestamp() ->
-  timestamp(calendar:universal_time()).
-
-timestamp(DateTime) ->
-  seconds(DateTime) - epoch().
-
-epoch() ->
-  seconds({{1970,1,1},{00,00,00}}).
-
-seconds(DateTime) ->
-  calendar:datetime_to_gregorian_seconds(DateTime).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/src/erlang-oauth/oauth_uri.erl
----------------------------------------------------------------------
diff --git a/src/erlang-oauth/oauth_uri.erl b/src/erlang-oauth/oauth_uri.erl
deleted file mode 100644
index aebf093..0000000
--- a/src/erlang-oauth/oauth_uri.erl
+++ /dev/null
@@ -1,100 +0,0 @@
--module(oauth_uri).
-
--export([normalize/1, calate/2, encode/1]).
--export([params_from_string/1, params_to_string/1,
-  params_from_header_string/1, params_to_header_string/1]).
-
--import(lists, [concat/1]).
-
-
-normalize(URI) ->
-  case http_uri:parse(URI) of
-    {ok, {Scheme, UserInfo, Host, Port, Path, _Query}} -> % R15B
-        normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
-    {Scheme, UserInfo, Host, Port, Path, _Query} ->
-      normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
-    Else ->
-      Else
-  end.
-
-normalize(http, UserInfo, Host, 80, Acc) ->
-  normalize(http, UserInfo, [Host|Acc]);
-normalize(https, UserInfo, Host, 443, Acc) ->
-  normalize(https, UserInfo, [Host|Acc]);
-normalize(Scheme, UserInfo, Host, Port, Acc) ->
-  normalize(Scheme, UserInfo, [Host, ":", Port|Acc]).
-
-normalize(Scheme, [], Acc) ->
-  concat([Scheme, "://"|Acc]);
-normalize(Scheme, UserInfo, Acc) ->
-  concat([Scheme, "://", UserInfo, "@"|Acc]).
-
-params_to_header_string(Params) ->
-  intercalate(", ", [concat([encode(K), "=\"", encode(V), "\""]) || {K, V} <- Params]).
-
-params_from_header_string(String) ->
-  [param_from_header_string(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
-
-param_from_header_string(Param) ->
-  [Key, QuotedValue] = string:tokens(Param, "="),
-  Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
-  {decode(Key), decode(Value)}.
-
-params_from_string(Params) ->
-  [param_from_string(Param) || Param <- string:tokens(Params, "&")].
-
-param_from_string(Param) ->
-  list_to_tuple([decode(Value) || Value <- string:tokens(Param, "=")]).
-
-params_to_string(Params) ->
-  intercalate("&", [calate("=", [K, V]) || {K, V} <- Params]).
-
-calate(Sep, Xs) ->
-  intercalate(Sep, [encode(X) || X <- Xs]).
-
-intercalate(Sep, Xs) ->
-  concat(intersperse(Sep, Xs)).
-
-intersperse(_, []) -> [];
-intersperse(_, [X]) -> [X];
-intersperse(Sep, [X|Xs]) ->
-  [X, Sep|intersperse(Sep, Xs)].
-
--define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
-
-encode(Term) when is_integer(Term) ->
-  integer_to_list(Term);
-encode(Term) when is_atom(Term) ->
-  encode(atom_to_list(Term));
-encode(Term) when is_list(Term) ->
-  encode(lists:reverse(Term, []), []).
-
-encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
-  encode(T, [X | Acc]);
-encode([X | T], Acc) ->
-  NewAcc = [$%, dec2hex(X bsr 4), dec2hex(X band 16#0f) | Acc],
-  encode(T, NewAcc);
-encode([], Acc) ->
-  Acc.
-
-decode(Str) when is_list(Str) ->
-  decode(Str, []).
-
-decode([$%, A, B | T], Acc) ->
-  decode(T, [(hex2dec(A) bsl 4) + hex2dec(B) | Acc]);
-decode([X | T], Acc) ->
-  decode(T, [X | Acc]);
-decode([], Acc) ->
-  lists:reverse(Acc, []).
-
--compile({inline, [{dec2hex, 1}, {hex2dec, 1}]}).
-
-dec2hex(N) when N >= 10 andalso N =< 15 ->
-  N + $A - 10;
-dec2hex(N) when N >= 0 andalso N =< 9 ->
-  N + $0.
-
-hex2dec(C) when C >= $A andalso C =< $F ->
-  C - $A + 10;
-hex2dec(C) when C >= $0 andalso C =< $9 ->
-  C - $0.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d74e93c9/test/etap/160-vhosts.t
----------------------------------------------------------------------
diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t
index 94882fe..46fdd73 100755
--- a/test/etap/160-vhosts.t
+++ b/test/etap/160-vhosts.t
@@ -335,7 +335,7 @@ test_vhost_request_with_oauth(Db) ->
 
     Url = "http://oauth-example.com/",
     Consumer = {"consec1", "foo", hmac_sha1},
-    SignedParams = oauth:signed_params(
+    SignedParams = oauth:sign(
         "GET", Url, [], Consumer, "otoksec1", "foobar"),
     OAuthUrl = oauth:uri(server(), SignedParams),
 
@@ -350,7 +350,7 @@ test_vhost_request_with_oauth(Db) ->
     end,
 
     Consumer2 = {"consec1", "bad_secret", hmac_sha1},
-    SignedParams2 = oauth:signed_params(
+    SignedParams2 = oauth:sign(
         "GET", Url, [], Consumer2, "otoksec1", "foobar"),
     OAuthUrl2 = oauth:uri(server(), SignedParams2),