How are the passwords hashed in auth.txt?

CalumMc
Member
Posts: 58
Joined: Mon Dec 12, 2011 16:50
Location: England

How are the passwords hashed in auth.txt?

by CalumMc » Post

I have searched the forum and the IRC log but I can't find any mention of the type of hashing used in this file. I am trying to find out because I would like to use this file as a way of authenticating server users for a web based script.

Thank you.
Last edited by CalumMc on Tue Aug 06, 2013 16:03, edited 1 time in total.

User avatar
xyz
Member
Posts: 450
Joined: Thu Nov 10, 2011 14:25

by xyz » Post

You should (probably) use custom auth handler.

Here's the code which hashes login, password pair:

Code: Select all

std::string translatePassword(std::string playername, std::wstring password)
{
    if(password.length() == 0)
        return "";

    std::string slt = playername + wide_to_narrow(password);
    SHA1 sha1;
    sha1.addBytes(slt.c_str(), slt.length());
    unsigned char *digest = sha1.getDigest();
    std::string pwd = base64_encode(digest, 20);
    free(digest);
    return pwd;
}
Be aware that server doesn't know client's raw password, it's transferred in hashed state.

Edit: Here's Python code

Code: Select all

hashed = base64.b64encode(hashlib.sha1("%s%s" % (name, password)).digest())[:-1]

CalumMc
Member
Posts: 58
Joined: Mon Dec 12, 2011 16:50
Location: England

by CalumMc » Post

Perfect! Thanks.

User avatar
BrandonReese
Member
Posts: 839
Joined: Wed Sep 12, 2012 00:44
GitHub: bremaweb
IRC: BrandonReese
In-game: BrandonReese
Location: USA

by BrandonReese » Post

CalumMc wrote:I have searched the forum and the IRC log but I can't find any mention of the type of hashing used in this file. I am trying to find out because I would like to use this file as a way of authenticating server users for a web based script.

Thank you.
I wrote about 80% of a web authentication mod (sign up on a website, minetest uses the REST api to authenticate with the website & retrieve permissions) and this is the PHP I used to generate the password hash from the information received from the user to check against the auth.txt file

Code: Select all

$hash = rtrim(base64_encode(sha1($username . $password,true)),"=")
Last edited by BrandonReese on Tue Aug 06, 2013 18:37, edited 1 time in total.

CalumMc
Member
Posts: 58
Joined: Mon Dec 12, 2011 16:50
Location: England

by CalumMc » Post

Thanks xyz and BrandonReese for your code snippets.

Exilyth
Member
Posts: 73
Joined: Sun Jul 28, 2013 18:46
Location: Earth

by Exilyth » Post

Imho, that hash function is lacking taste. Could use a bit more salt... ;)
I'm running 0.4.13 stable with [technic][carts][farming_plus][biome_lib][unified_inventory] and a few other mods.

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

With the new minetest versions (I am using minetest 0.4.13), this is not anymore true.
Indeed, if I call core.get_password_hash(player_name, player_password), the returned value is not the one stored in the auth.txt file.
Can you explain me how I can get the real stored value please?

sfan5
Moderator
Posts: 4095
Joined: Wed Aug 24, 2011 09:44
GitHub: sfan5
IRC: sfan5
Location: Germany

Re: How are the passwords hashed in auth.txt?

by sfan5 » Post

Newer Minetest versions use SRP for password storage if the client supports it.
The only thing you can do now is check stuff stored in the auth.txt against a plaintext password (or during login while interacting with the client).
Mods: Mesecons | WorldEdit | Nuke & Minetest builds for Windows (32-bit & 64-bit)

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

OK so how I can do this check in the C++ source code of Minetest please?
What's the name of the function that can check the SRP password (stored in auth.txt) against a plaintext password (given by the user)?

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: How are the passwords hashed in auth.txt?

by rubenwardy » Post

It's a whole protocol exchange, not a hashing function. If you're trying to make IRC work, don't bother. Just use /setpassword for not to use the old hashing function.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

In fact, I am writing in C an utility to manage Minetest worlds.
It reads the content of the auth.txt file and checks if the provided nickname and password matches with a user in the auth.txt file.
It works well with old passwords because I have understood how the old passwords are hashed looking at the Minetest source code.
But with new passwords, I don't understand well.
I am not familiar with SRP and reading the Minetest C++ source code does not help me to understood better.
I would like that my utility supports the new passwords too.
Is it technically possible?

sfan5
Moderator
Posts: 4095
Joined: Wed Aug 24, 2011 09:44
GitHub: sfan5
IRC: sfan5
Location: Germany

Re: How are the passwords hashed in auth.txt?

by sfan5 » Post

It is technically possible to check the SRP values from auth.txt against a plaintext password. SRP is not meant to be used that way though, the whole point of SRP is that you don't need to know your users password to check them.
If you absolutely need to do this I suggest you to read up on an SRP implementation for C (you need to pick one with strict RFC 5054 compatibility as Minetest uses a library with this special thing).
Mods: Mesecons | WorldEdit | Nuke & Minetest builds for Windows (32-bit & 64-bit)

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: How are the passwords hashed in auth.txt?

by rubenwardy » Post

This is the library MT uses: https://github.com/est31/csrp-gmp
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

OK I have written a minimal C code using this SRP library in order to authenticate a MT user.
Unfortunately, it does not work.

This is the code:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include <ctype.h> /* for tolower */
#include "srp.h" /* for srp_create_salted_verification_key */
char *minetest_test_srp_string_tolower(const char *string)
{
	int index = 0;
	int len = strlen(string);
	char *result = NULL;
	result = malloc(sizeof(char) * (len + 1));
	if(result == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the string containing lower case letters.\n");
		return NULL;
	}
	for(; index < len; index++)
		result += tolower(string[index]);
	result[len] = '\0';
	return result;
}
int minetest_test_srp(const char *name, const char *password)
{
	char *name_lower = minetest_test_srp_string_tolower(name);
	struct SRPUser *usr = NULL;
	struct SRPVerifier *ver = NULL;
	unsigned char *bytes_s = NULL;
	unsigned char *bytes_v = NULL;
	unsigned char *bytes_A = 0;
	unsigned char *bytes_B = 0;
	unsigned char *bytes_M = 0;
	unsigned char *bytes_HAMK = 0;
	const unsigned char *bytes_S = 0;
	size_t len_s = 0;
	size_t len_v = 0;
	size_t len_A = 0;
	size_t len_B = 0;
	size_t len_M = 0;
	size_t len_S = 0;
	srp_create_salted_verification_key(SRP_SHA256,
			SRP_NG_2048,
			name_lower,
			(const unsigned char *) password,
			strlen(password),
			&bytes_s,
			&len_s,
			&bytes_v,
			&len_v,
			NULL,
			NULL);
	if(bytes_s == NULL || bytes_v == NULL)
	{
		fprintf(stderr, "Unable to create the SRP salted verification key.\n");
		return -1;
	}
	usr = srp_user_new(SRP_SHA256,
			SRP_NG_2048,
			name,
			name,
			(const unsigned char *) password,
			strlen(password),
			NULL,
			NULL);
	if(usr == NULL)
	{
		fprintf(stderr, "Unable to create the SRP user.\n");
		return -1;
	}
	srp_user_start_authentication(usr,
			NULL,
			NULL,
			0,
			&bytes_A,
			&len_A);
	if(bytes_A == NULL)
	{
		fprintf(stderr, "Unable to start the SRP user authentification.\n");
		return -1;
	}
	ver = srp_verifier_new(SRP_SHA256,
			SRP_NG_2048,
			name,
			bytes_s,
			len_s,
			bytes_v,
			len_v,
			bytes_A,
			len_A,
			NULL,
			0,
			&bytes_B,
			&len_B,
			NULL,
			NULL);
	if(ver == NULL || bytes_B == NULL)
	{
		fprintf(stderr, "Unable to create the SRP verifier.\n");
		return -1;
	}
	srp_user_process_challenge(usr,
			NULL,
			len_s,
			bytes_B,
			len_B,
			&bytes_M,
			&len_M);
	if(bytes_M == NULL)
	{
		fprintf(stderr, "Unable to process the SRP user challenge.\n");
		return -1;
	}
	srp_verifier_verify_session(ver,
			bytes_M,
			&bytes_HAMK);
	if(bytes_HAMK == NULL)
	{
		fprintf(stderr, "Unable to verify the SRP verifier session.\n");
		return -1;
	}
	if(!srp_user_is_authenticated(usr))
		return 0;
	bytes_S = srp_verifier_get_session_key(ver, &len_S);
	if(bytes_S == NULL)
	{
		fprintf(stderr, "Unable to get the SRP session key.\n");
		return -1;
	}
	return 1;
}
int main(int argc, char **argv)
{
	if(argc != 3)
	{
		fprintf(stderr, "Usage: %s NAME PASSWORD\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	int result = minetest_test_srp(argv[1], argv[2]);
	if(result == -1)
	{
		fprintf(stderr, "Unable to authenticate through the SRP protocol.\n");
		exit(EXIT_FAILURE);
	}
	if(result == 1)
		printf("Authentification: Success!\n");
	else
		printf("Authentification: Failure!\n");
	exit(EXIT_SUCCESS);
}
This is how to run it:

Code: Select all

$ ./minetest_test_srp.out 
Usage: ./minetest_test_srp.out NAME PASSWORD
The gdb backtrace is:

Code: Select all

$ gdb --args ./minetest_test_srp.out MyName MyPassword
GNU gdb (GDB) 7.6.1
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-slackware-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/yugiohjcj/documents/projects/software/minetest-test-srp/minetest-test-srp-current/minetest_test_srp.out...done.
(gdb) run
Starting program: /home/yugiohjcj/documents/projects/software/minetest-test-srp/minetest-test-srp-current/./minetest_test_srp.out MyName MyPassword

Program received signal SIGSEGV, Segmentation fault.
0xb7e2c097 in memcpy () from /lib/libc.so.6
(gdb) bt
#0  0xb7e2c097 in memcpy () from /lib/libc.so.6
#1  0x0804941a in H_ns (result=0xbfffec6c, alg=SRP_SHA256, n=0x0, len_n=16, 
    bytes=0xbfffebb0 "g\347\370\331\326\322:\265\372\t\377\234#\233\331\336r\027_o\260x\327\fE\370\253ڐ\001\344\341\004", len_bytes=32) at srp.c:419
#2  0x08049537 in calculate_x (result=0xbfffec6c, alg=SRP_SHA256, salt=0x0, salt_len=16, username=0x8053418 "MyName", password=0x8053428 "MyPassword", password_len=10)
    at srp.c:443
#3  0x0804a754 in srp_user_process_challenge (usr=0x8053078, bytes_s=0x0, len_s=16, 
    bytes_B=0x8054148 "b\273\060\311Z=\311\023\251\321\067\225]Ҽ\355\332aHN\034\227\a')$\240\331\377k\204\066\256C\016\262\061\271.\024:D\354\221\fa\275\377i\344ə1\323v%\362A\217\275\220\255\256\302\315n\306\003\336\334\344\066\060\367 \027\226\237]\202Ki%\236\006}\216,\325\335^\234&\032+\224\326@\325m\361\201r|h\t֛:wsN&\360½<S\246\325\067\371\032bl\004\257\256cy\226\261\330\034\271\215fPA\326RaU\242\361f\274\033*\272#c`C\322J\251\276", len_B=256, bytes_M=0xbfffecfc, len_M=0xbfffece4)
    at srp.c:972
#4  0x08048de5 in minetest_test_srp (name=0xbffff012 "MyName", password=0xbffff019 "MyPassword") at minetest_test_srp.c:100
#5  0x08048f02 in main (argc=3, argv=0xbfffee14) at minetest_test_srp.c:137
(gdb) quit
A debugging session is active.

	Inferior 1 [process 2610] will be killed.

Quit anyway? (y or n) y
If you see where are the mistakes in my code please tell me.
I provide the full source code archive [1] containing the Makefile, the SRP library and my code.

[1] http://yugiohjcj.free.fr/minetest-test- ... 420.tar.xz

est31
Developer
Posts: 173
Joined: Mon Dec 29, 2014 01:49

Re: How are the passwords hashed in auth.txt?

by est31 » Post

YuGiOhJCJ your code is failing in srp_user_process_challenge because you set a salt param of NULL. The salt needs to be known by that function. It doesn't crash anymore if the salt gets used from above. I have no idea however why it then still doesn't work. I see some confusion around name/name_for_verifier, but otherwise I haven't looked closely what may be wrong too.

But what do you generally want to achieve? Verify that a user auth.txt entry matches the given password? Then your approach is wrong, you will only need srp_create_salted_verification_key for that.

You can look at test_srp.c for an example for how to use csrp-gmp.

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

Indeed, I gave the NULL value for the 2nd parameter of the srp_user_process_challenge function.
It means that I gave a NULL salt to this function and that's why my program crashes.
I am wondering what salt value I should use.

Anyway, like you said, as my objective is to check that a given name/password matches with a player in my auth.txt file, then I only need the srp_create_salted_verification_key function for that.
That should reduce the lines of my code :)

So, after I call srp_create_salted_verification_key, what should I do to check if the provided name/password matches with a player in my auth.txt file?
In other words, how to do the comparison between bytes_s, bytes_v and the value stored in my auth.txt file?

In fact, I am already using the test_srp.c file as example to create my code.
If you watch the code in test_srp.c, you will see that I am using the same variable names and the same order of function calls.
I have just done some modifications in order to match with what MT is doing in its source code (for example: I am using SRP_SHA256 instead of SRP_SHA1).

This is the new code:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include <ctype.h> /* for tolower */
#include "srp.h" /* for srp_create_salted_verification_key */
char *minetest_test_srp_string_tolower(const char *string)
{
	int index = 0;
	int len = strlen(string);
	char *result = NULL;
	result = malloc(sizeof(char) * (len + 1));
	if(result == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the string containing lower case letters.\n");
		return NULL;
	}
	for(; index < len; index++)
		result += tolower(string[index]);
	result[len] = '\0';
	return result;
}
int minetest_test_srp(const char *name, const char *password, const char *verification_key)
{
	char *name_lower = minetest_test_srp_string_tolower(name);
	unsigned char *bytes_s = NULL;
	unsigned char *bytes_v = NULL;
	size_t len_s = 0;
	size_t len_v = 0;
	unsigned char *bytes_verification_key = (unsigned char *) verification_key;
	int index = 0;
	srp_create_salted_verification_key(SRP_SHA256,
			SRP_NG_2048,
			name_lower,
			(const unsigned char *) password,
			strlen(password),
			&bytes_s,
			&len_s,
			&bytes_v,
			&len_v,
			NULL,
			NULL);
	if(bytes_s == NULL || bytes_v == NULL)
	{
		fprintf(stderr, "Unable to create the SRP salted verification key.\n");
		return -1;
	}
	for(; index < len_s; index++)
		if(bytes_s[index] != bytes_verification_key[index])
			return 1;
	return 0;
}
int main(int argc, char **argv)
{
	if(argc != 4)
	{
		fprintf(stderr, "Usage: %s NAME PASSWORD VERIFICATION_KEY\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	int result = minetest_test_srp(argv[1], argv[2], argv[3]);
	switch(result)
	{
		case -1:
			fprintf(stderr, "Unable to authenticate through the SRP protocol.\n");
			exit(EXIT_FAILURE);
			break;
		case 0:
			printf("Authentication: Success!\n");
			break;
		default:
			printf("Authentication: Failure!\n");
			break;
	}
	exit(EXIT_SUCCESS);
}
This is how to run it now:

Code: Select all

$ ./minetest_test_srp.out 
Usage: ./minetest_test_srp.out NAME PASSWORD VERIFICATION_KEY
The authentication is a failure:

Code: Select all

$ ./minetest_test_srp.out "MyName" "MyPassword" "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
Authentication: Failure!
I provide the new source code archive [1] containing the Makefile, the SRP library and my code.

[1] http://yugiohjcj.free.fr/minetest-test- ... 421.tar.xz

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

OK I am not yet able to authenticate a user through SRP but I have enhanced my code.
Now, I create a verification key from the given name and password that has the same size than the one in the auth.txt file.
In fact, I have checked again the MT source code and I have remarked that the verification key stored in the auth.txt file is the concatenation of "#1#" + the salt encoded with base64 + "#" + the verifier encoded with base64 [1].

Based on this new information, I have rewritten my source code:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include <ctype.h> /* for tolower */
#include "base64.h" /* for base64_encode */
#include "srp.h" /* for srp_create_salted_verification_key */
char *minetest_test_srp_string_tolower(const char *string)
{
	int index = 0;
	int len = strlen(string);
	char *result = NULL;
	result = malloc(sizeof(char) * (len + 1));
	if(result == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the string containing lower case letters.\n");
		return NULL;
	}
	for(; index < len; index++)
		result[index] = tolower(string[index]);
	result[len] = '\0';
	return result;
}
int minetest_test_srp(const char *name, const char *password, const char *verification_key)
{
	char *name_lower = minetest_test_srp_string_tolower(name);
	char *base64_bytes_s = NULL;
	char *base64_bytes_v = NULL;
	char *verification_key_to_check = NULL;
	unsigned char *bytes_s = NULL;
	unsigned char *bytes_v = NULL;
	size_t len_s = 0;
	size_t len_v = 0;
	srp_create_salted_verification_key(SRP_SHA256,
			SRP_NG_2048,
			name_lower,
			(const unsigned char *) password,
			strlen(password),
			&bytes_s,
			&len_s,
			&bytes_v,
			&len_v,
			NULL,
			NULL);
	free(name_lower);
	if(bytes_s == NULL)
	{
		fprintf(stderr, "Unable to create the SRP salt.\n");
		return -1;
	}
	if(bytes_v == NULL)
	{
		fprintf(stderr, "Unable to create the SRP verifier.\n");
		return -1;
	}
	base64_bytes_s = base64_encode(bytes_s, len_s);
	if(base64_bytes_s == NULL)
	{
		fprintf(stderr, "Unable to encode with base64 the SRP salt.\n");
		return -1;
	}
	base64_bytes_v = base64_encode(bytes_v, len_v);
	if(base64_bytes_v == NULL)
	{
		fprintf(stderr, "Unable to encode with base64 the SRP verifier.\n");
		return -1;
	}
	verification_key_to_check = malloc(sizeof(char) * (3 + strlen(base64_bytes_s) + 1 + strlen(base64_bytes_v) + 1));
	if(verification_key_to_check == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the SRP verification key to check.\n");
		return -1;
	}
	sprintf(verification_key_to_check, "#1#%s#%s", base64_bytes_s, base64_bytes_v);
	printf("verification_key = \"%s\"\n", verification_key);
	printf("strlen(verification_key) = \"%d\"\n", strlen(verification_key));
	printf("verification_key_to_check = \"%s\"\n", verification_key_to_check);
	printf("strlen(verification_key_to_check) = \"%d\"\n", strlen(verification_key_to_check));
	free(base64_bytes_s);
	free(base64_bytes_v);
	free(bytes_s);
	free(bytes_v);
	if(strcmp(verification_key, verification_key_to_check) != 0)
	{
		free(verification_key_to_check);
		return 1;
	}
	free(verification_key_to_check);
	return 0;
}
int main(int argc, char **argv)
{
	int result = -1;
	if(argc != 4)
	{
		fprintf(stderr, "Usage: %s NAME PASSWORD VERIFICATION_KEY\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	result = minetest_test_srp(argv[1], argv[2], argv[3]);
	switch(result)
	{
		case -1:
			fprintf(stderr, "Unable to authenticate through the SRP protocol.\n");
			exit(EXIT_FAILURE);
			break;
		case 0:
			printf("Authentication: Success!\n");
			break;
		default:
			printf("Authentication: Failure!\n");
			break;
	}
	exit(EXIT_SUCCESS);
}
But the authentication is still a failure because the two verification keys (the one computed from the given name/password and the one directly given by the user) are not equal:

Code: Select all

$ ./minetest_test_srp.out "MyName" "MyPassword" "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key = "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
strlen(verification_key) = "368"
verification_key_to_check = "#1#7jYZpHTZUAvHSfJx+fLcsQ#U/wWqrAx08CcnCu620WkmH9VNKbtLxXx0sVzCBxV9OLVej2c+sOK12loGeef4AI7l2OuOgtiUWlqXrJ5w/JZLUG31XOfu/musva95RemjoBv/7EbvLZyHY1LohQt19QECbEfCw59JcEcM+NspjO3h18EC3LeN36ch9Q42o3ujss13kUxbtR7ZSvZVy1X091bw9F6rCe1k46J0vULq2WEVllFzGMSMeSfphtnQg16RUU1JvvRyDEgq5tneNpm2ioGxh/UXGkszlOxSNPPCo965EDbiFEYLJEB6Af4sdiYo9WpeRGpAIvoZ7CGkQkNeVjkm3wtKGYIhYbhC7SF51qogQ"
strlen(verification_key_to_check) = "368"
Authentication: Failure!
What is fun (but it is probably part of the SRP protocol) is that every time I create a verification key from the given name and password, I get a totally new different verification key:

Code: Select all

$ ./minetest_test_srp.out "MyName" "MyPassword" "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key = "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
strlen(verification_key) = "368"
verification_key_to_check = "#1#0vgyjGL+wn9SGJOXiuq6xA#fFbFFZg1T+y8Pp5jgISonYw32u2nsK0rXrJpXg7WFQIbMHgKbSVjetc3erG/P6P4XjixMKS0FgUEfbUJejtzdyXo1p90dKGxnkEaPqjEBtX06MTDDaVXLQVXG3Eafi7dxx41gFfrx4/j0Vo/ckQ5nE2AQyiLYfKx6fBXuQD315VPdSC7BWZGdk4tDtClIRJjtAqG98XfvhJnOrqUDUimJO57IPj+DR0RgNvkPQE6clZHzYBUrw5VZBDfzJGziJckyF9JUErU5MKxcXU76/tki5uw8TY0JahUvoLrhMoU4+l8gza2dwU18j2W+t+Nw28o/qnsdaUP1UdhZ2CRzm/vJw"
strlen(verification_key_to_check) = "368"
Authentication: Failure!
So, comparing the two verification keys is probably not the correct way to authenticate an user with SRP.
Well, if you know what I should do after calling the srp_create_salted_verification_key function, please tell me.

I provide the new source code archive [2] containing the Makefile, the SRP library, the base64 library and my code.

[1] https://github.com/minetest/minetest/bl ... h.cpp#L103
[2] http://yugiohjcj.free.fr/minetest-test- ... 504.tar.xz

est31
Developer
Posts: 173
Joined: Mon Dec 29, 2014 01:49

Re: How are the passwords hashed in auth.txt?

by est31 » Post

No, you have to compare the verification keys, but you must not let it autogenerate the salt, but use the salt provided to the program. That's your problem I think.

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

OK, so I need to provide a non-NULL value to the 6th parameter of the srp_create_salted_verification_key function in order to give a fixed salt that will not be auto-generated.
I am wondering what value I need to give.
As I already have the verification key in the auth.txt file, I guess I need to extract from this string the part corresponding to the salt (from the 4th character to the 25th character of this string).
As it is encoded with base64, I guess I need to decode this sub-string corresponding to the salt before giving it to the srp_create_salted_verification_key function.
Is it correct?

est31
Developer
Posts: 173
Joined: Mon Dec 29, 2014 01:49

Re: How are the passwords hashed in auth.txt?

by est31 » Post

Yes, that's precisely what you need to do.

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

Thank you it works :)

Here is the new code:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include <ctype.h> /* for tolower */
#include "base64.h" /* for base64_encode */
#include "srp.h" /* for srp_create_salted_verification_key */
char *minetest_test_srp_string_tolower(const char *string)
{
	int index = 0;
	int len = strlen(string);
	char *result = NULL;
	result = malloc(sizeof(char) * (len + 1));
	if(result == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the string containing lower case letters.\n");
		return NULL;
	}
	for(; index < len; index++)
		result[index] = tolower(string[index]);
	result[len] = '\0';
	return result;
}
int minetest_test_srp(const char *name, const char *password, const char *base64_salt, const char *base64_verifier)
{
	char *verification_key = NULL;
	char *salt = NULL;
	char *name_lower = minetest_test_srp_string_tolower(name);
	char *base64_bytes_s = NULL;
	char *base64_bytes_v = NULL;
	char *verification_key_to_check = NULL;
	unsigned char *bytes_s = NULL;
	unsigned char *bytes_v = NULL;
	size_t len_s = 0;
	size_t len_v = 0;
	verification_key = malloc(sizeof(char) * (3 + strlen(base64_salt) + 1 + strlen(base64_verifier) + 1));
	if(verification_key == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the SRP verification key.\n");
		free(name_lower);
		return -1;
	}
	sprintf(verification_key, "#1#%s#%s", base64_salt, base64_verifier);
	salt = base64_decode(base64_salt);
	if(salt == NULL)
	{
		fprintf(stderr, "Unable to decode with base64 the SRP salt.\n");
		free(name_lower);
		free(verification_key);
		return -1;
	}
	bytes_s = (unsigned char *) salt;
	len_s = strlen(bytes_s);
	srp_create_salted_verification_key(SRP_SHA256,
			SRP_NG_2048,
			name_lower,
			(const unsigned char *) password,
			strlen(password),
			&bytes_s,
			&len_s,
			&bytes_v,
			&len_v,
			NULL,
			NULL);
	free(name_lower);
	if(bytes_s == NULL)
	{
		fprintf(stderr, "Unable to create the SRP salt.\n");
		free(verification_key);
		return -1;
	}
	base64_bytes_s = base64_encode(bytes_s, len_s);
	if(base64_bytes_s == NULL)
	{
		fprintf(stderr, "Unable to encode with base64 the SRP salt.\n");
		free(verification_key);
		return -1;
	}
	if(bytes_v == NULL)
	{
		fprintf(stderr, "Unable to create the SRP verifier.\n");
		free(verification_key);
		return -1;
	}
	base64_bytes_v = base64_encode(bytes_v, len_v);
	if(base64_bytes_v == NULL)
	{
		fprintf(stderr, "Unable to encode with base64 the SRP verifier.\n");
		free(verification_key);
		return -1;
	}
	verification_key_to_check = malloc(sizeof(char) * (3 + strlen(base64_bytes_s) + 1 + strlen(base64_bytes_v) + 1));
	if(verification_key_to_check == NULL)
	{
		fprintf(stderr, "Unable to allocate memory for the SRP verification key to check.\n");
		free(verification_key);
		return -1;
	}
	sprintf(verification_key_to_check, "#1#%s#%s", base64_bytes_s, base64_bytes_v);
	printf("verification_key = \"#1#%s#%s\"\n", base64_salt, base64_verifier);
	printf("verification_key_to_check = \"%s\"\n", verification_key_to_check);
	free(base64_bytes_s);
	free(base64_bytes_v);
	free(bytes_s);
	free(bytes_v);
	if(strcmp(verification_key, verification_key_to_check) != 0)
	{
		free(verification_key);
		free(verification_key_to_check);
		return 1;
	}
	free(verification_key);
	free(verification_key_to_check);
	return 0;
}
int main(int argc, char **argv)
{
	int result = -1;
	if(argc != 5)
	{
		fprintf(stderr, "Usage: %s NAME PASSWORD BASE64_SALT BASE64_VERIFIER\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	result = minetest_test_srp(argv[1], argv[2], argv[3], argv[4]);
	switch(result)
	{
		case -1:
			fprintf(stderr, "Unable to authenticate through the SRP protocol.\n");
			exit(EXIT_FAILURE);
			break;
		case 0:
			printf("Authentication: Success!\n");
			break;
		default:
			printf("Authentication: Failure!\n");
			break;
	}
	exit(EXIT_SUCCESS);
}
Here is how to use it:

Code: Select all

$ ./minetest_test_srp.out 
Usage: ./minetest_test_srp.out NAME PASSWORD BASE64_SALT BASE64_VERIFIER
NAME is the name that you can find in the auth.txt file. It is the first field of a line delimited with the ":" character.
PASSWORD is the password that you must know.
BASE64_SALT is the salt encoded with base64 that you can find in the auth.txt file. It is a part of the second field of a line delimited with the ":" character. It can be found between the "#1#" characters and the "#" character.
BASE64_VERIFIER is the verifier encoded with base64 that you can find in the auth.txt file. It is a part of the second field of a line delimited with the ":" character. It can be found after the 3rd "#" character.

Here is how it works with a successful authentication:

Code: Select all

./minetest_test_srp.out "MyName" "MyPassword" "2VEDb5dBstV5FAGoISg4tw" "L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key = "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key_to_check = "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
Authentication: Success!
Here is how it works with an authentication failure:

Code: Select all

$ ./minetest_test_srp.out "MyName" "MyWrongPassword" "2VEDb5dBstV5FAGoISg4tw" "L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key = "#1#2VEDb5dBstV5FAGoISg4tw#L53b5qo+8YOCtijt5918ps38vxZFxp2aS9YvRBvc2zTXOj+r9zzFuregY5SzxJOr0ARp6CmWPpG9y1a51sFEh3EvdmupH08/3UtTAM9MmwW/6eOrXp2PgWooercXzZbyzDU91tqFXhTv9xtew8JD84MhJeqnOWL4nFe7yDuQrU2rN71NMhIuFtMubpRCTpTvHjKqnLKqJ0cBGls7/cAcQArvzTuHNLwuTXi0aDU4D2Pf4VxTOlsM16HY/Z36rAi3SbCg2fkCMm7qo9h5LVYUeq08/UlUdBXPV9I4m2xwOEWF49ctfFcqd8d/l3EJLPca+gGWYCyqOzt0ty/kjui5xQ"
verification_key_to_check = "#1#2VEDb5dBstV5FAGoISg4tw#O/u4C6lKWYWeFwEiYunXjOj9FwZTXaEcNbsBuEqb1e0scJ3vPh0jCI24DT2zbFl+HYiZIzoNeCCrDUKoxshy2jVocqqulDmWD8zXfNjDU4tZ2c1bcQGt9QZqIK114F8Lyw/L62jv6aDdkFLzSbqwQhjGUzc8eNSwrOF6TZrQ+MLowgnUjHt5WKKeTXpomerPW7gOp3a7QZqeiV3jhK8ATwJCKHnHWWw3BvAWuqzkEItjXf1BF+ppSnbvMvFXWmLYqRvwPkSpM/9xUu6AJ8queg2XYXEa9t813LvqCwFQe//cTC9QrSudrvKvMAErdh82fJPnxGUNzHyyykkUTxnJUQ"
Authentication: Failure!
I provide the new source code archive [1] containing the Makefile, the SRP library, the base64 library and my code.

[1] http://yugiohjcj.free.fr/minetest-test- ... 508.tar.xz

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

I found a case where it does not work :(

Here is the content of my auth.txt file:

Code: Select all

$ cat ~/.minetest/worlds/myworld/auth.txt
MyName:#1#IFrOhR2wlVmgEUYWreUArA#OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A:interact,home,teleport,give,shout,interaction_areas_user:1464919128
Here is the call to my program:

Code: Select all

$ ./minetest_test_srp.out MyName MyPassword "IFrOhR2wlVmgEUYWreUArA" "OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A"
verification_key = "#1#IFrOhR2wlVmgEUYWreUArA#OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A"
verification_key_to_check = "#1#IFrOhR2wlVmgEUYWreU#BHdKeqysUwHyYM0L+1kddDVKnA1cl1PAaDff3HtQ2g6AICNJfbx98igyeFiJ7hkFTwBHssjlaGmNTrLeVibzPrgFsWVnapYyu1L5pQ9jWU4nG5b9ZYR4vtfFnlOZ6egHbblE6nv2U/sXTqX5oVcp1QXmvPr+7IxwUIVGaC640fhgFBBfsbbe+gJz8oNNmmtcfh2d018ZTz5ugZ1jtYpWFY34J3qqwaeNtdH+UsRN9Y4QslQJKHgzPdf0TJq1qOAB3E6rOdpTjCsvDto4YWd0V8NG/QZV1IriRlCHsfFZsWUCOoF0aIlyur3cRUr1pPbAy+UHkhkIsR1fIcKMHdp28g"
Authentication: Failure!
We can see that the salt that I give is: "IFrOhR2wlVmgEUYWreUArA" (size is 22 characters).
However, if you check the salt after the call to the srp_create_salted_verification_key function, the value is: "IFrOhR2wlVmgEUYWreU" (size is 19 characters).
Yes, you guessed it: Three characters are missing in this salt (these characters are: "ArA").
As the salt has been changed, the generated key is also different.

Do you know why my program fails in this case please?

est31
Developer
Posts: 173
Joined: Mon Dec 29, 2014 01:49

Re: How are the passwords hashed in auth.txt?

by est31 » Post

No idea, maybe check with valgrind that no writes happen outside of ranges where they shouldn't happen?

User avatar
YuGiOhJCJ
Member
Posts: 38
Joined: Sat Jan 23, 2016 07:41
GitHub: YuGiOhJCJ
IRC: YuGiOhJCJ
In-game: YuGiOhJCJ
Contact:

Re: How are the passwords hashed in auth.txt?

by YuGiOhJCJ » Post

It seems that valgrind does not detect any error:

Code: Select all

$ valgrind ./minetest_test_srp.out MyName MyPassword "IFrOhR2wlVmgEUYWreUArA" "OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A"
==975== Memcheck, a memory error detector
==975== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==975== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==975== Command: ./minetest_test_srp.out MyName MyPassword IFrOhR2wlVmgEUYWreUArA OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A
==975== 
verification_key = "#1#IFrOhR2wlVmgEUYWreUArA#OdnUTPArLAGNlsqXz1GB4lAosMTZQKS04XwrPpAq+j++EZabl5OIHbwhqwkFT16ijAcT0ziqQsvven34HaDwoitLlE1nAtIT82mT7SC5IVsnCF8J1/SlQ2dMIm4+tmpVe4A4tHlHyQVHjEuOlNaTnat7QykLNZFZbF9BrK1jqnkvHH8zMoGsf7ClOTYejPWWe2j+jLm6xz5h6nASQdSxY34smGDhYKhcok0asSAgDLJILMuA0FTV6U4dY7gjKWbZCiTt5ueLje9fcB1OMS/1t9NCxvylp1p7R9u9PqLhjBnMlDtzy0uyj6kcvS2C0kWg67/PNYRVcmtjGxGGYRGU/A"
verification_key_to_check = "#1#IFrOhR2wlVmgEUYWreU#BHdKeqysUwHyYM0L+1kddDVKnA1cl1PAaDff3HtQ2g6AICNJfbx98igyeFiJ7hkFTwBHssjlaGmNTrLeVibzPrgFsWVnapYyu1L5pQ9jWU4nG5b9ZYR4vtfFnlOZ6egHbblE6nv2U/sXTqX5oVcp1QXmvPr+7IxwUIVGaC640fhgFBBfsbbe+gJz8oNNmmtcfh2d018ZTz5ugZ1jtYpWFY34J3qqwaeNtdH+UsRN9Y4QslQJKHgzPdf0TJq1qOAB3E6rOdpTjCsvDto4YWd0V8NG/QZV1IriRlCHsfFZsWUCOoF0aIlyur3cRUr1pPbAy+UHkhkIsR1fIcKMHdp28g"
Authentication: Failure!
==975== 
==975== HEAP SUMMARY:
==975==     in use at exit: 0 bytes in 0 blocks
==975==   total heap usage: 392 allocs, 392 frees, 61,348 bytes allocated
==975== 
==975== All heap blocks were freed -- no leaks are possible
==975== 
==975== For counts of detected and suppressed errors, rerun with: -v
==975== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
However, I think I found something interesting.

I have rewritten the code in order to focus on the salt, the base64_decode function and the base64_encode function:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include "base64.h" /* for base64_encode */
int main(int argc, char **argv)
{
	char *salt = NULL;
	int len_salt = 0;
	char *base64_salt = NULL;
	if(argc != 2)
	{
		fprintf(stderr, "Usage: %s BASE64_SALT\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	salt = base64_decode(argv[1]);
	len_salt = strlen(salt);
	base64_salt = base64_encode((unsigned char *) salt, len_salt);
	printf("argv[1] = \"%s\"\n", argv[1]);
	printf("salt = \"%s\"\n", salt);
	printf("len_salt = \"%d\"\n", len_salt);
	printf("base64_salt = \"%s\"\n", base64_salt);
	free(salt);
	free(base64_salt);
	exit(EXIT_SUCCESS);
}
Here is how to use it:

Code: Select all

$ ./minetest_test_srp.out 
Usage: ./minetest_test_srp.out BASE64_SALT
Here is an example:

Code: Select all

$ ./minetest_test_srp.out "IFrOhR2wlVmgEUYWreUArA"
argv[1] = "IFrOhR2wlVmgEUYWreUArA"
salt = " Z΅°Y F­å"
len_salt = "14"
base64_salt = "IFrOhR2wlVmgEUYWreU"
As you can see, I give an encoded base64 salt, then I decode it, then I encode it again and the result is that I don't get the same encoded base64 salt.
I provide the new source code archive [1] containing the Makefile, the base64 library and my code.

I have also rewritten the code in C++:

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include "base64.h" /* for base64_encode */
int main(int argc, char **argv)
{
	std::string salt;
	int len_salt = 0;
	std::string base64_salt;
	if(argc != 2)
	{
		fprintf(stderr, "Usage: %s BASE64_SALT\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	salt = base64_decode(argv[1]);
	len_salt = salt.size();
	base64_salt = base64_encode((unsigned char *) salt.c_str(), len_salt);
	printf("argv[1] = \"%s\"\n", argv[1]);
	printf("salt = \"%s\"\n", salt.c_str());
	printf("len_salt = \"%d\"\n", len_salt);
	printf("len_salt = \"%d\"\n", strlen(salt.c_str()));
	printf("base64_salt = \"%s\"\n", base64_salt.c_str());
	for(int index = 0; index < len_salt; index++)
		printf("salt[%d] = \"%c\" (is null character? \"%d\")\n", index, salt[index], salt[index] == '\0');
	printf("sizeof(char) = \"%d\"\n", sizeof(char));
	exit(EXIT_SUCCESS);
}
The usage is the same:

Code: Select all

$ ./minetest_test_srp_cpp.out 
Usage: ./minetest_test_srp_cpp.out BASE64_SALT
Here is an example:

Code: Select all

$ ./minetest_test_srp_cpp.out "IFrOhR2wlVmgEUYWreUArA"
argv[1] = "IFrOhR2wlVmgEUYWreUArA"
salt = " Z΅°Y F­å"
len_salt = "16"
len_salt = "14"
base64_salt = "IFrOhR2wlVmgEUYWreUArA"
salt[0] = " " (is null character? "0")
salt[1] = "Z" (is null character? "0")
salt[2] = "Î" (is null character? "0")
salt[3] = "" (is null character? "0")
salt[4] = "" (is null character? "0")
salt[5] = "°" (is null character? "0")
salt[6] = "" (is null character? "0")
salt[7] = "Y" (is null character? "0")
salt[8] = " " (is null character? "0")
salt[9] = "" (is null character? "0")
salt[10] = "F" (is null character? "0")
salt[11] = "" (is null character? "0")
salt[12] = "­" (is null character? "0")
salt[13] = "å" (is null character? "0")
salt[14] = "" (is null character? "1")
salt[15] = "¬" (is null character? "0")
sizeof(char) = "1"
As you can see, in this C++ version it works!
I give an encoded base64 salt, then I decode it, then I encode it again and the result is that I get the same encoded base64 salt.

Why it works here?
The reason is that instead of using the strlen function (the one we use with C strings), I am using here the size member function (the one we use with C++ strings).
There is a difference between these two functions.
The one from C is computing the size of the string by looking for the first occurrence of the '\0' character in the string.
The one from C++ does not do that, which means that C++ strings can contain a '\0' character.
Unfortunately, the base64_decode function can return a string containing a '\0' character, so the strlen function is not able to compute correctly the size of the string.
As you can see the character at index 14 of the returned string is a '\0', so the length computed is 14 instead of 16.

I provide the C++ source code archive [2] containing the Makefile, the base64 library and my code.

The solution in C is to return the string and the real size of the string (not the one computed with strlen):

Code: Select all

#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <string.h> /* for strlen */
#include "base64.h" /* for base64_encode */
int main(int argc, char **argv)
{
	char *salt = NULL;
	int len_salt = 0;
	char *base64_salt = NULL;
	if(argc != 2)
	{
		fprintf(stderr, "Usage: %s BASE64_SALT\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	salt = base64_decode(argv[1], &len_salt);
	base64_salt = base64_encode((unsigned char *) salt, len_salt);
	printf("argv[1] = \"%s\"\n", argv[1]);
	printf("salt = \"%s\"\n", salt);
	printf("len_salt = \"%d\"\n", len_salt);
	printf("base64_salt = \"%s\"\n", base64_salt);
	free(salt);
	free(base64_salt);
	exit(EXIT_SUCCESS);
}
Here is the result:

Code: Select all

$ ./minetest_test_srp.out "IFrOhR2wlVmgEUYWreUArA"
argv[1] = "IFrOhR2wlVmgEUYWreUArA"
salt = " Z΅°Y F­å"
len_salt = "16"
base64_salt = "IFrOhR2wlVmgEUYWreUArA"
It works in C too!

I provide the new C source code archive [3] containing the Makefile, the base64 library and my code.

[1] http://yugiohjcj.free.fr/minetest-test- ... 604.tar.xz
[2] http://yugiohjcj.free.fr/minetest-test- ... 605.tar.xz
[3] http://yugiohjcj.free.fr/minetest-test- ... 605.tar.xz

wemopoj361
New member
Posts: 4
Joined: Tue Apr 11, 2023 23:31

Re: How are the passwords hashed in auth.txt?

by wemopoj361 » Post

CalumMc wrote:
Tue Aug 06, 2013 16:03
I have searched the forum and the IRC log but I can't find any mention of the type of hashing used in this file. I am trying to find out because I would like to use this file as a way of authenticating server users for a web based script.

Thank you.
That would depend on your minetest version.

Legacy format (until 0.4.12) of password hash is <name><password> SHA1'd,
in the base64 encoding.
Format (since 0.4.13) is a SRP salt and verifier pair.


This forum may help: viewtopic.php?f=47&t=20517&p=423474#p423474

Locked

Who is online

Users browsing this forum: Bing [Bot] and 31 guests