You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/12/04 21:09:46 UTC

[15/22] couch commit: updated refs/heads/2491-refactor-couch-httpd-auth to 3e8286d

TOTP secrets are stored in Base32


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

Branch: refs/heads/2491-refactor-couch-httpd-auth
Commit: bfbf8ebed19ba511fc50f916f3fd41cea84e815f
Parents: 6125862
Author: Robert Newson <rn...@apache.org>
Authored: Sun Nov 2 17:15:49 2014 +0000
Committer: Robert Newson <rn...@apache.org>
Committed: Sun Nov 2 18:09:18 2014 +0000

----------------------------------------------------------------------
 src/couch_base32.erl        | 127 +++++++++++++++++++++++++++++++++++++++
 src/couch_httpd_auth.erl    |   2 +-
 test/couch_base32_tests.erl |  28 +++++++++
 3 files changed, 156 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/bfbf8ebe/src/couch_base32.erl
----------------------------------------------------------------------
diff --git a/src/couch_base32.erl b/src/couch_base32.erl
new file mode 100644
index 0000000..d8d754f
--- /dev/null
+++ b/src/couch_base32.erl
@@ -0,0 +1,127 @@
+% 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_base32).
+
+-export([encode/1, decode/1]).
+
+-define(SET, <<"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567">>).
+
+
+-spec encode(binary()) -> binary().
+encode(Plain) when is_binary(Plain) ->
+    IoList = encode(Plain, 0, byte_size(Plain) * 8, []),
+    iolist_to_binary(lists:reverse(IoList)).
+
+encode(_Plain, _ByteOffset, 0, Acc) ->
+    Acc;
+
+encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 8 ->
+    <<A:5, B:3>> = binary:part(Plain, ByteOffset, 1),
+    [<<(binary:at(?SET, A)),
+       (binary:at(?SET, B bsl 2)),
+       "======">> | Acc];
+
+encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 16 ->
+    <<A:5, B:5, C:5, D:1>> = binary:part(Plain, ByteOffset, 2),
+    [<<(binary:at(?SET, A)),
+       (binary:at(?SET, B)),
+       (binary:at(?SET, C)),
+       (binary:at(?SET, D bsl 4)),
+       "====">> | Acc];
+
+encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 24 ->
+    <<A:5, B:5, C:5, D:5, E:4>> = binary:part(Plain, ByteOffset, 3),
+    [<<(binary:at(?SET, A)),
+       (binary:at(?SET, B)),
+       (binary:at(?SET, C)),
+       (binary:at(?SET, D)),
+       (binary:at(?SET, E bsl 1)),
+       "===">> | Acc];
+
+encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 32 ->
+    <<A:5, B:5, C:5, D:5, E:5, F:5, G:2>> = binary:part(Plain, ByteOffset, 4),
+    [<<(binary:at(?SET, A)),
+       (binary:at(?SET, B)),
+       (binary:at(?SET, C)),
+       (binary:at(?SET, D)),
+       (binary:at(?SET, E)),
+       (binary:at(?SET, F)),
+       (binary:at(?SET, G bsl 3)),
+       "=">> | Acc];
+
+encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining >= 40 ->
+    <<A:5, B:5, C:5, D:5, E:5, F:5, G:5, H:5>> =
+        binary:part(Plain, ByteOffset, 5),
+    Output = <<(binary:at(?SET, A)),
+               (binary:at(?SET, B)),
+               (binary:at(?SET, C)),
+               (binary:at(?SET, D)),
+               (binary:at(?SET, E)),
+               (binary:at(?SET, F)),
+               (binary:at(?SET, G)),
+               (binary:at(?SET, H))>>,
+    encode(Plain, ByteOffset + 5, BitsRemaining  - 40, [Output | Acc]).
+
+
+-spec decode(binary()) -> binary().
+decode(Encoded) when is_binary(Encoded) ->
+    IoList = decode(Encoded, 0, []),
+    iolist_to_binary(lists:reverse(IoList)).
+
+decode(Encoded, ByteOffset, Acc) when ByteOffset == byte_size(Encoded) ->
+    Acc;
+decode(Encoded, ByteOffset, Acc) ->
+    case binary:part(Encoded, ByteOffset, 8) of
+        <<A:1/binary, B:1/binary, "======">> ->
+            [<<(find_in_set(A)):5,
+               (find_in_set(B) bsr 2):3>> | Acc];
+        <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, "====">> ->
+            [<<(find_in_set(A)):5,
+               (find_in_set(B)):5,
+               (find_in_set(C)):5,
+               (find_in_set(D) bsr 4):1>> | Acc];
+        <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, E:1/binary, "===">> ->
+            [<<(find_in_set(A)):5,
+               (find_in_set(B)):5,
+               (find_in_set(C)):5,
+               (find_in_set(D)):5,
+               (find_in_set(E) bsr 1):4>> | Acc];
+        <<A:1/binary, B:1/binary, C:1/binary, D:1/binary,
+          E:1/binary, F:1/binary, G:1/binary, "=">> ->
+            [<<(find_in_set(A)):5,
+               (find_in_set(B)):5,
+               (find_in_set(C)):5,
+               (find_in_set(D)):5,
+               (find_in_set(E)):5,
+               (find_in_set(F)):5,
+               (find_in_set(G) bsr 3):2>> | Acc];
+        <<A:1/binary, B:1/binary, C:1/binary, D:1/binary,
+          E:1/binary, F:1/binary, G:1/binary, H:1/binary>> ->
+            decode(Encoded, ByteOffset + 8,
+                   [<<(find_in_set(A)):5,
+                      (find_in_set(B)):5,
+                      (find_in_set(C)):5,
+                      (find_in_set(D)):5,
+                      (find_in_set(E)):5,
+                      (find_in_set(F)):5,
+                      (find_in_set(G)):5,
+                      (find_in_set(H)):5>> | Acc])
+    end.
+
+find_in_set(Char) ->
+    case binary:match(?SET, Char) of
+        nomatch ->
+            erlang:error(not_base32);
+        {Offset, _} ->
+            Offset
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/bfbf8ebe/src/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl
index cda51c5..5e528d9 100644
--- a/src/couch_httpd_auth.erl
+++ b/src/couch_httpd_auth.erl
@@ -448,7 +448,7 @@ verify_totp(User, Form) ->
         undefined ->
             ok;
         {Props} ->
-            Key = couch_util:get_value(<<"key">>, Props),
+            Key = couch_base32:decode(couch_util:get_value(<<"key">>, Props)),
             Alg = couch_util:to_existing_atom(
                 couch_util:get_value(<<"algorithm">>, Props, <<"sha">>)),
             Len = couch_util:get_value(<<"length">>, Props, 6),

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/bfbf8ebe/test/couch_base32_tests.erl
----------------------------------------------------------------------
diff --git a/test/couch_base32_tests.erl b/test/couch_base32_tests.erl
new file mode 100644
index 0000000..7e4d59a
--- /dev/null
+++ b/test/couch_base32_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_base32_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+base32_test() ->
+    roundtrip(<<"">>, <<"">>),
+    roundtrip(<<"f">>, <<"MY======">>),
+    roundtrip(<<"fo">>, <<"MZXQ====">>),
+    roundtrip(<<"foo">>, <<"MZXW6===">>),
+    roundtrip(<<"foob">>, <<"MZXW6YQ=">>),
+    roundtrip(<<"fooba">>, <<"MZXW6YTB">>),
+    roundtrip(<<"foobar">>, <<"MZXW6YTBOI======">>).
+
+roundtrip(Plain, Encoded) ->
+    ?assertEqual(Plain, couch_base32:decode(Encoded)),
+    ?assertEqual(Encoded, couch_base32:encode(Plain)).