문제

I am trying to implement SRP6 in Erlang using the crypto module but I cannot get the session keys to match. I am using a 256 bit prime number. When I use a 1024 bit prime number, they match though. I have left out the optional scrambler param in crypto:compute_key, but it makes no difference.

Can anyone help me figure out why my session keys don't match?

-module(srp).
-export([test/0]).


getUsername() -> <<"alice">>.

getPassword() -> <<"password123">>.

getSalt() -> <<"mystrongsalt">>.

getGenerator() -> <<7>>.

%% srp version 6
getVersion() -> '6'.

% randomly generated 32 byte number
getClientPrivate() ->
    <<16#C49F832EE8D67ECF9E7F2785EB0622D8B3FE2344C00F96E1AEF4103CA44D51F9:256>>.

% randomly generated 32 byte number
getServerPrivate() ->
    <<16#6C78CCEAAEC15E69068A87795B2A20ED7B45CFC5A254EBE2F17F144A4D99DB18:256>>.


%% 32 byte prime number
%% used in mangos: https://github.com/mangoszero/server/blob/master/src/realmd/AuthSocket.cpp#L190
%% used in arcemu: http://arcemu.org/wiki/Server_Logon_Challenge
getPrime() ->
    <<16#894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7:256>>.



%% v = g^x
getVerifier() ->
    Generator = getGenerator(),
    Prime = getPrime(),
    DerivedKey = getDerivedKey(),
    crypto:mod_pow(Generator, DerivedKey, Prime).


%% x = H(salt, H(username, :, password))
getDerivedKey() ->
    Username = getUsername(),
    Password = getPassword(),
    Salt = getSalt(),
    crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]).



%% client public key
getClientPublic() ->
    PrivateKey = getClientPrivate(),
    Generator = getGenerator(),
    Prime = getPrime(),
    Version = getVersion(),
        {Pub, PrivateKey} = crypto:generate_key(srp, {user, [Generator, Prime, Version]}, PrivateKey),
    Pub.

%% server public key
getServerPublic() ->
    PrivateKey = getServerPrivate(),
    Generator = getGenerator(),
    Prime = getPrime(),
    Version = getVersion(),
    Verifier = getVerifier(),
    {Pub, PrivateKey} = crypto:generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivateKey),
    Pub.


%% client session key
computeClientKey() ->
    ServerPublic = getServerPublic(),
    ClientPrivate = getClientPrivate(),
    ClientPublic = getClientPublic(),
    Generator = getGenerator(),
    Prime = getPrime(),
    Version = getVersion(),
    DerivedKey = getDerivedKey(),
    crypto:compute_key(srp, ServerPublic, {ClientPublic, ClientPrivate}, {user, [DerivedKey, Prime, Generator, Version]}).

%% server session key
computeServerKey() ->
    ClientPublic = getClientPublic(),
    ServerPrivate = getServerPrivate(),
    ServerPublic = getServerPublic(),
    Prime = getPrime(),
    Version = getVersion(),
    Verifier = getVerifier(),
    crypto:compute_key(srp, ClientPublic, {ServerPublic, ServerPrivate}, {host, [Verifier, Prime, Version]}).



test() ->
    %% these session keys should match
    ClientKey = computeClientKey(),
    ServerKey = computeServerKey(),
    io:format("client skey: ~p~n", [ClientKey]),
    io:format("server skey: ~p~n", [ServerKey]),
    ClientKey == ServerKey.

gist can be found here https://gist.github.com/jcclinton/4cdc9f616927677737a2

도움이 되었습니까?

해결책

Unfortunately, this is a bug in OTP SRP implementation affecting the computation of the client key.

There is a fix available: https://github.com/erlang/otp/pull/369

Alternatively, you can compute client key yourself, something along these lines.

computeClientKey() ->
    ServerPublic = getServerPublic(),
    ClientPrivate = getClientPrivate(),
    ClientPublic = getClientPublic(),
    Generator = getGenerator(),
    Prime = getPrime(),
    Version = getVersion(),
    DerivedKey = getDerivedKey(),

    Multiplier = getMultiplier(Version, Generator, Prime),

    U = crypto:hash(sha, [padded(ClientPublic, Prime), padded(ServerPublic, Prime)]),
    PrimeI = bin_to_int(Prime),
    BX = crypto:mod_pow(Generator, DerivedKey, Prime),
    BTMPI0 = bin_to_int(ServerPublic) - bin_to_int(Multiplier) * bin_to_int(BX),
    BTMPI = BTMPI0 rem PrimeI,
    Base = if
        BTMPI > 0 -> int_to_bin(BTMPI);
        true -> int_to_bin(BTMPI + PrimeI)
    end,
    Exponent = int_to_bin(bin_to_int(ClientPrivate) + bin_to_int(U) * bin_to_int(DerivedKey)),
    crypto:mod_pow(Base, Exponent, Prime).

padded(V, N) when byte_size(V) =:= byte_size(N) -> V;
padded(V, N) when byte_size(V) < byte_size(N) ->
    NLen = byte_size(N),
    PadLen = (NLen - byte_size(V)) * 8,
    Pad = <<0:PadLen>>,
    [Pad, V].

int_to_bin(Int) ->
    Len0 = length(erlang:integer_to_list(Int, 16)),
    Len1 = Len0 + (Len0 rem 2),
    Bits = Len1 * 4,
    <<Int:Bits>>.

bin_to_int(Bin) ->
    Bits = byte_size(Bin) * 8,
    <<Val:Bits>> = Bin,
    Val.

getMultiplier('6a', Generator, Prime) ->
    crypto:hash(sha, [Prime, padded(Generator, Prime)]);
getMultiplier('6', _Generator, _Prime) ->
    <<3/integer>>;
getMultiplier('3', _Generator, _Prime) ->
    <<1/integer>>.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top