mirror of
https://github.com/valitydev/shamir.git
synced 2024-11-06 01:35:18 +00:00
Implement Shamir secret sharing and recovery.
This commit is contained in:
commit
dab8df7189
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
ebin
|
||||
*~
|
11
Makefile
Normal file
11
Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
ERL ?= erl
|
||||
APP := shamir
|
||||
|
||||
all:
|
||||
@./rebar compile
|
||||
|
||||
clean:
|
||||
@./rebar clean
|
||||
|
||||
test: all
|
||||
@(./rebar skip_deps=true eunit)
|
83
src/galois.erl
Normal file
83
src/galois.erl
Normal file
@ -0,0 +1,83 @@
|
||||
%% Copyright 2011 Robert Newson
|
||||
%%
|
||||
%% 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(galois).
|
||||
|
||||
-export([generate/1, add/3, subtract/3, multiply/3, divide/3]).
|
||||
-define(nw(W), (1 bsl W)).
|
||||
|
||||
-record(galois, {
|
||||
w,
|
||||
gflog,
|
||||
gfilog
|
||||
}).
|
||||
|
||||
generate(W) ->
|
||||
generate(W, 1, 0).
|
||||
|
||||
generate(W, B, Log) ->
|
||||
generate(W, B, Log, dict:new(), dict:new()).
|
||||
|
||||
generate(W, _B, Log, Gflog, Gfilog) when Log =:= ?nw(W) -1 ->
|
||||
#galois{w=W, gflog=Gflog, gfilog=Gfilog};
|
||||
generate(W, B, Log, Gflog, Gfilog) ->
|
||||
Gflog1 = dict:store(B, Log, Gflog),
|
||||
Gfilog1 = dict:store(Log, B, Gfilog),
|
||||
B1 = B bsl 1,
|
||||
B2 = if
|
||||
B1 band ?nw(W) > 0 ->
|
||||
B1 bxor prim_poly(W);
|
||||
true ->
|
||||
B1
|
||||
end,
|
||||
generate(W, B2, Log + 1, Gflog1, Gfilog1).
|
||||
|
||||
multiply(#galois{}, 0, _) ->
|
||||
0;
|
||||
multiply(#galois{}, _, 0) ->
|
||||
0;
|
||||
multiply(#galois{w=W, gflog=Gflog, gfilog=Gfilog}, A, B) ->
|
||||
case dict:fetch(A, Gflog) + dict:fetch(B, Gflog) of
|
||||
SumLog when SumLog >= ?nw(W) - 1 ->
|
||||
dict:fetch(SumLog - (?nw(W) - 1), Gfilog);
|
||||
SumLog ->
|
||||
dict:fetch(SumLog, Gfilog)
|
||||
end.
|
||||
|
||||
divide(#galois{}, 0, _) ->
|
||||
0;
|
||||
divide(#galois{}, _, 0) ->
|
||||
throw(division_by_zero);
|
||||
divide(#galois{w=W, gflog=Gflog, gfilog=Gfilog}, A, B) ->
|
||||
case dict:fetch(A, Gflog) - dict:fetch(B, Gflog) of
|
||||
DiffLog when DiffLog < 0 ->
|
||||
dict:fetch(DiffLog + (?nw(W) - 1), Gfilog);
|
||||
DiffLog ->
|
||||
dict:fetch(DiffLog, Gfilog)
|
||||
end.
|
||||
|
||||
add(#galois{}, A, B) ->
|
||||
A bxor B.
|
||||
|
||||
subtract(#galois{}, A, B) ->
|
||||
A bxor B.
|
||||
|
||||
prim_poly(4) ->
|
||||
8#23;
|
||||
prim_poly(8) ->
|
||||
8#435;
|
||||
prim_poly(16) ->
|
||||
8#210013.
|
||||
|
||||
|
29
src/shamir.app.src
Normal file
29
src/shamir.app.src
Normal file
@ -0,0 +1,29 @@
|
||||
%% Copyright 2011 Robert Newson
|
||||
%%
|
||||
%% 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.
|
||||
|
||||
%%-*- mode: erlang -*-
|
||||
{application, shamir,
|
||||
[
|
||||
{description, "shamir"},
|
||||
{vsn, git},
|
||||
{modules, []},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib,
|
||||
crypto
|
||||
]},
|
||||
{mod, { shamir, []}},
|
||||
{env, []}
|
||||
]}.
|
81
src/shamir.erl
Normal file
81
src/shamir.erl
Normal file
@ -0,0 +1,81 @@
|
||||
%% Copyright 2011 Robert Newson
|
||||
%%
|
||||
%% 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(shamir).
|
||||
|
||||
-export([share/3, recover/1]).
|
||||
|
||||
-record(share, {
|
||||
threshold,
|
||||
x,
|
||||
y
|
||||
}).
|
||||
|
||||
share(Secret, Threshold, Count) when is_binary(Secret) ->
|
||||
share(binary_to_list(Secret), Threshold, Count);
|
||||
|
||||
share(Secret, Threshold, Count) when is_list(Secret) ->
|
||||
Shares = transpose([share(Byte, Threshold, Count) || Byte <- Secret]),
|
||||
[#share{threshold=Threshold, x=X, y=list_to_binary(lists:nth(X, Shares))}
|
||||
|| X <- lists:seq(1, Count)];
|
||||
|
||||
share(Secret, Threshold, Count) when (Secret >= 0 andalso Secret =< 255) ->
|
||||
GF = galois:generate(8),
|
||||
Coeffs = binary_to_list(crypto:rand_bytes(Threshold - 1)) ++ [Secret],
|
||||
[horner(GF, X, Coeffs) || X <- lists:seq(1, Count)].
|
||||
|
||||
horner(GF, X, Coeffs) ->
|
||||
horner(GF, X, Coeffs, 0).
|
||||
|
||||
horner(_, _, [], Acc) ->
|
||||
Acc;
|
||||
horner(GF, X, [Coeff|Rest], Acc) ->
|
||||
Mult = galois:multiply(GF, Acc, X),
|
||||
Add = galois:add(GF, Mult, Coeff),
|
||||
horner(GF, X, Rest, Add).
|
||||
|
||||
recover([#share{threshold=Threshold}|_]=Shares0) ->
|
||||
Shares = lists:ukeysort(#share.x, Shares0),
|
||||
X = [X || #share{x=X} <- Shares],
|
||||
Ys = transpose(lists:map(fun(#share{y=Y}) ->
|
||||
binary_to_list(Y) end, Shares)),
|
||||
list_to_binary([recover(Threshold, Z) || Z <- [lists:zip(X, Y) || Y <- Ys]]).
|
||||
|
||||
recover(Threshold, Shares) when length(Shares) >= Threshold ->
|
||||
lagrange(lists:sublist(Shares, Threshold)).
|
||||
|
||||
lagrange(Shares) ->
|
||||
GF = galois:generate(8),
|
||||
lists:foldl(fun(Share, Acc) ->
|
||||
galois:add(GF, lagrange(GF, Share, Shares), Acc) end,
|
||||
0, Shares).
|
||||
|
||||
lagrange(GF, Share, Shares) ->
|
||||
lagrange(GF, Share, Shares, 1).
|
||||
|
||||
lagrange(GF, {_, Y}, [], Acc) ->
|
||||
galois:multiply(GF, Y, Acc);
|
||||
lagrange(GF, {X, Y}, [{X, _} | Rest], Acc) ->
|
||||
lagrange(GF, {X, Y}, Rest, Acc);
|
||||
lagrange(GF, {X1, Y1}, [{X2, _} | Rest], Acc) ->
|
||||
lagrange(GF, {X1, Y1}, Rest,
|
||||
galois:multiply(GF, Acc,
|
||||
galois:divide(GF, X2,
|
||||
galois:subtract(GF, X1, X2)))).
|
||||
|
||||
transpose([[X | Xs] | Xss]) ->
|
||||
[[X | [H || [H | _] <- Xss]]
|
||||
| transpose([Xs | [T || [_ | T] <- Xss]])];
|
||||
transpose([[] | Xss]) -> transpose(Xss);
|
||||
transpose([]) -> [].
|
22
test/shamir_test.erl
Normal file
22
test/shamir_test.erl
Normal file
@ -0,0 +1,22 @@
|
||||
%% Copyright 2011 Robert Newson
|
||||
%%
|
||||
%% 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(shamir_test).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
shamir_test() ->
|
||||
Secret = <<"hello">>,
|
||||
Shares = shamir:share(Secret, 3, 4),
|
||||
RecoveredSecret= shamir:recover(Shares),
|
||||
?assertEqual(Secret, RecoveredSecret).
|
Loading…
Reference in New Issue
Block a user