Search code examples
ejabberdmongoose-im

MongooseIM/ejabberd: http auth using scram


I am currently playing around a little bit with MongooseIM and want to use HTTP auth together with scram. I am using python passlib to create scram hashes:

import sys
from passlib.hash import scram


def main():
    hash = scram.encrypt(sys.argv[1], rounds=4096, salt_size=16)
    print hash


if __name__ == "__main__":
    main()

Then I end up with something like this:

$scram$4096$BmAsRcgZA4AwRijl3FtLyQ$sha-1=AXh5FzYzEnf6PaVQNR79AZhkwz8,sha-256=YZceXCVhfCBrr8sM9k3eS.5bztHugerGzjO97emvn20,sha-512=2NyVspiE7MP6xBAEycAV5Z/nIbBlki3sHfWvVUPPnEkMt5b4VbZfDZ0s8lvE/ns0scPGWmfKhUobmZbjfFH6RA

Unfortunately this format is not accepted by MongooseIM's HTTP auth. I had a look at the code and tried to find out how the serialzed form of scram hashed passwords looks like here: https://github.com/esl/MongooseIM/blob/master/apps/ejabberd/src/scram.erl

deserialize(<<?SCRAM_SERIAL_PREFIX, Serialized/binary>>) ->
    case catch binary:split(Serialized, <<",">>, [global]) of
        [StoredKey, ServerKey,Salt,IterationCount] ->
            {ok, #scram{storedkey = StoredKey,
                        serverkey = ServerKey,
                        salt = Salt,
                        iterationcount = binary_to_integer(IterationCount)}};
        _ ->
            ?WARNING_MSG("Incorrect serialized SCRAM: ~p, ~p", [Serialized]),
            {error, incorrect_scram}
    end;

From passlib I get the salt, the iteration count and the actual digest (sha-1, sha-256, sha-512) of the salted (hashed) password as far as I understood, but what about the StoredKey and the ServerKey from the Erlang code? How would a correct serialized HTTP body returned by host/get_password look like?

Thanks in advance, Magnus


Solution

  • so I figured it out and wrote a little python script to generate the expected format.

    import base64
    import hashlib
    import hmac
    import sys
    from passlib.hash import scram
    
    
    # password_to_scram(Password, IterationCount) ->
    #     Salt = crypto:rand_bytes(?SALT_LENGTH),
    #     SaltedPassword = salted_password(Password, Salt, IterationCount),
    #     StoredKey = stored_key(scram:client_key(SaltedPassword)),
    #     ServerKey = server_key(SaltedPassword),
    #     #scram{storedkey = base64:encode(StoredKey),
    #            serverkey = base64:encode(ServerKey),
    #            salt = base64:encode(Salt),
    #            iterationcount = IterationCount}.
    def main():
        rounds = 4096
        hash = scram.encrypt(sys.argv[1], rounds=rounds, salt_size=16)
        hash = scram.encrypt('1234', rounds=rounds, salt='salt')
        salt, iterations, salted_password = scram.extract_digest_info(hash, "sha-1")
    
        # server_key(SaltedPassword) ->
        # crypto:hmac(sha, SaltedPassword, <<"Server Key">>).
        server_key = hmac.new(key=salted_password, msg='Server Key', digestmod=hashlib.sha1).digest()
    
        # client_key(SaltedPassword) ->
        # crypto:hmac(sha, SaltedPassword, <<"Client Key">>).
        client_key = hmac.new(key=salted_password, msg='Client Key', digestmod=hashlib.sha1).digest()
    
        # StoredKey = stored_key(scram:client_key(SaltedPassword)),
        stored_key = hashlib.sha1(client_key).digest()
    
        result = '==SCRAM==,%s,%s,%s,%d' % \
              (base64.b64encode(stored_key), base64.b64encode(server_key), base64.b64encode(salt), rounds)
    
        print result
    
    if __name__ == '__main__':
        main()
    

    Verification:

    (mongooseim@localhost)2> base64:encode(scram:salted_password(<<"1234">>, <<"salt">>, 4096)).    
    <<"vbpf4gmRPxs/+ru4TZJO3toJdw0=">>
    
    (mongooseim@localhost)4> base64:encode(scram:stored_key(scram:client_key(scram:salted_password(<<"1234">>, <<"salt">>, 4096)))).
    <<"bXKEekOUoWNAx0f21H/fIZ4dj6Y=">>
    
    (mongooseim@localhost)3> base64:encode(scram:server_key(scram:salted_password(<<"1234">>, <<"salt">>, 4096))).
    <<"eVwl7wTir232HDy7Tzq3SXZHn+4=">>
    
    ==SCRAM==,bXKEekOUoWNAx0f21H/fIZ4dj6Y=,eVwl7wTir232HDy7Tzq3SXZHn+4=,c2FsdA==,4096