Skip to content

Commit 4222462

Browse files
committed
Implement 'passwordHash' and 'passwordVerify'
1 parent ca791c4 commit 4222462

24 files changed

+3010
-0
lines changed

Shared/mods/deathmatch/logic/Enums.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ IMPLEMENT_ENUM_BEGIN( EHashFunction::EHashFunctionType )
4646
ADD_ENUM ( EHashFunction::SHA512, "sha512" )
4747
IMPLEMENT_ENUM_END( "hash-function" )
4848

49+
IMPLEMENT_ENUM_CLASS_BEGIN(PasswordHashFunction)
50+
ADD_ENUM(PasswordHashFunction::Bcrypt, "bcrypt")
51+
IMPLEMENT_ENUM_CLASS_END("password-hash-function")
52+
4953
IMPLEMENT_ENUM_BEGIN( ePacketID )
5054
ADD_ENUM1( PACKET_ID_SERVER_JOIN )
5155
ADD_ENUM1( PACKET_ID_SERVER_JOIN_DATA )

Shared/mods/deathmatch/logic/Enums.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ enum eEulerRotationOrder
6464
DECLARE_ENUM( eEulerRotationOrder );
6565

6666
DECLARE_ENUM( EHashFunction::EHashFunctionType );
67+
DECLARE_ENUM_CLASS(PasswordHashFunction);
68+
6769
DECLARE_ENUM( ePacketID );

Shared/mods/deathmatch/logic/luadefs/CLuaCryptDefs.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ void CLuaCryptDefs::LoadFunctions ( void )
1919
CLuaCFunctions::AddFunction ( "teaDecode", TeaDecode );
2020
CLuaCFunctions::AddFunction ( "base64Encode", Base64encode );
2121
CLuaCFunctions::AddFunction ( "base64Decode", Base64decode );
22+
CLuaCFunctions::AddFunction("passwordHash", PasswordHash);
23+
CLuaCFunctions::AddFunction("passwordVerify", PasswordVerify);
2224
}
2325

2426
int CLuaCryptDefs::Md5 ( lua_State* luaVM )
@@ -173,3 +175,78 @@ int CLuaCryptDefs::Base64decode ( lua_State* luaVM )
173175
lua_pushboolean ( luaVM, false );
174176
return 1;
175177
}
178+
179+
int CLuaCryptDefs::PasswordHash(lua_State* luaVM)
180+
{
181+
// string password_hash(string password, string algorithm, table options = {})
182+
SString password;
183+
PasswordHashFunction algorithm;
184+
std::unordered_map<SString, SString> options;
185+
186+
CScriptArgReader argStream(luaVM);
187+
argStream.ReadString(password);
188+
argStream.ReadEnumString(algorithm);
189+
argStream.ReadStringMap(options, true);
190+
191+
if (!argStream.HasErrors())
192+
{
193+
if (algorithm == PasswordHashFunction::Bcrypt)
194+
{
195+
// Set default value to 10
196+
if (options["cost"].empty())
197+
options["cost"] = "10";
198+
199+
std::stringstream ss(options["cost"]);
200+
std::size_t cost;
201+
ss >> cost;
202+
203+
if (!ss.fail())
204+
{
205+
SString hash = SharedUtil::BcryptHash(password, options["salt"], cost);
206+
if (!hash.empty())
207+
{
208+
lua_pushstring(luaVM, hash);
209+
return 1;
210+
}
211+
else
212+
m_pScriptDebugging->LogCustom(luaVM, "Invalid value for field 'salt'");
213+
}
214+
else
215+
m_pScriptDebugging->LogWarning(luaVM, "Invalid value for field 'cost'");
216+
}
217+
}
218+
else
219+
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());
220+
221+
lua_pushboolean(luaVM, false);
222+
return 1;
223+
}
224+
225+
int CLuaCryptDefs::PasswordVerify(lua_State* luaVM)
226+
{
227+
// bool passwordVerify(string password, string hash)
228+
SString password;
229+
SString hash;
230+
231+
CScriptArgReader argStream(luaVM);
232+
argStream.ReadString(password);
233+
argStream.ReadString(hash);
234+
235+
if (!argStream.HasErrors())
236+
{
237+
if (hash.BeginsWith("$2y$"))
238+
{
239+
lua_pushboolean(luaVM, SharedUtil::BcryptVerify(password, hash));
240+
return 1;
241+
}
242+
else
243+
{
244+
m_pScriptDebugging->LogWarning(luaVM, "Passed unknown hash");
245+
}
246+
}
247+
else
248+
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());
249+
250+
lua_pushboolean(luaVM, false);
251+
return 1;
252+
}

Shared/mods/deathmatch/logic/luadefs/CLuaCryptDefs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ class CLuaCryptDefs : public CLuaDefs
2323
LUA_DECLARE ( TeaDecode );
2424
LUA_DECLARE ( Base64encode );
2525
LUA_DECLARE ( Base64decode );
26+
LUA_DECLARE(PasswordHash);
27+
LUA_DECLARE(PasswordVerify);
2628
};

Shared/sdk/SharedUtil.Hash.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ namespace EHashFunction
2424
}
2525
using EHashFunction::EHashFunctionType;
2626

27+
enum class PasswordHashFunction
28+
{
29+
Bcrypt
30+
};
31+
2732
namespace SharedUtil
2833
{
2934
struct MD5
@@ -80,6 +85,9 @@ namespace SharedUtil
8085
unsigned int HashString ( const char* szString );
8186
unsigned int HashString ( const char* szString, unsigned int length );
8287

88+
SString BcryptHash (const SString& password, SString salt = "", std::size_t cost = 10);
89+
bool BcryptVerify (const SString& password, const SString& hash);
90+
8391
SString ConvertDataToHexString ( const void* pData, uint uiLength );
8492
void ConvertHexStringToData ( const SString& strString, void* pOutData, uint uiLength );
8593
void GenerateSha256 ( const void* pData, uint uiLength, uchar output[32] );

Shared/sdk/SharedUtil.Hash.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212

1313
#include "sha1.hpp"
1414
#include "sha2.hpp"
15+
#include <random>
16+
#include <algorithm>
17+
18+
namespace bcrypt
19+
{
20+
extern "C"
21+
{
22+
#include <bcrypt/ow-crypt.h>
23+
}
24+
}
1525

1626
namespace SharedUtil
1727
{
@@ -479,6 +489,43 @@ namespace SharedUtil
479489
return c;
480490
}
481491

492+
constexpr const std::size_t HashBufferSize = 60 + 1;
493+
SString BcryptHash(const SString& password, SString salt, std::size_t cost)
494+
{
495+
if (salt.empty())
496+
{
497+
std::random_device random;
498+
std::mt19937 generator(random());
499+
500+
char saltBuffer[32];
501+
std::generate_n(saltBuffer, sizeof(saltBuffer), generator);
502+
503+
char saltBase64Buffer[30];
504+
bcrypt::crypt_gensalt_rn("$2y$", cost, saltBuffer, sizeof(saltBuffer), saltBase64Buffer, sizeof(saltBase64Buffer));
505+
salt = SStringX(saltBase64Buffer);
506+
}
507+
else
508+
{
509+
salt = SString("$2y$%02u$%s", cost, salt.substr(0, 22).c_str());
510+
}
511+
512+
if (salt.size() != 29)
513+
return "";
514+
515+
char hashBuffer[HashBufferSize];
516+
bcrypt::crypt_rn(password.c_str(), salt.c_str(), hashBuffer, sizeof(hashBuffer));
517+
518+
return SStringX(hashBuffer);
519+
}
520+
521+
bool BcryptVerify(const SString& password, const SString& hash)
522+
{
523+
char checkedHashBuffer[HashBufferSize];
524+
bcrypt::crypt_rn(password.c_str(), hash.c_str(), checkedHashBuffer, sizeof(checkedHashBuffer));
525+
526+
return strcmp(checkedHashBuffer, hash.c_str()) == 0;
527+
}
528+
482529

483530
SString ConvertDataToHexString( const void* pData, uint uiLength )
484531
{

premake5.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ workspace "MTASA"
3131
"vendor",
3232
"Shared/sdk",
3333
}
34+
35+
-- I know linking bcrypt here is ugly, but SharedUtil depends on it, thus it's required everywhere
36+
links { "bcrypt" }
3437

3538
defines {
3639
"_CRT_SECURE_NO_WARNINGS",
@@ -134,6 +137,7 @@ workspace "MTASA"
134137
include "Shared/XML"
135138

136139
group "Vendor"
140+
include "vendor/bcrypt"
137141
include "vendor/cryptopp"
138142
include "vendor/ehs"
139143
include "vendor/google-breakpad"

vendor/bcrypt/LINKS

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
New versions of this package (crypt_blowfish):
2+
3+
http://www.openwall.com/crypt/
4+
5+
A paper on the algorithm that explains its design decisions:
6+
7+
http://www.usenix.org/events/usenix99/provos.html
8+
9+
Unix Seventh Edition Manual, Volume 2: the password scheme (1978):
10+
11+
http://plan9.bell-labs.com/7thEdMan/vol2/password
12+
13+
The Openwall GNU/*/Linux (Owl) tcb suite implementing the alternative
14+
password shadowing scheme. This includes a PAM module which
15+
supersedes pam_unix and uses the password hashing framework provided
16+
with crypt_blowfish when setting new passwords.
17+
18+
http://www.openwall.com/tcb/
19+
20+
pam_passwdqc, a password strength checking and policy enforcement
21+
module for PAM-aware password changing programs:
22+
23+
http://www.openwall.com/passwdqc/
24+
25+
John the Ripper password cracker:
26+
27+
http://www.openwall.com/john/
28+
29+
$Owl: Owl/packages/glibc/crypt_blowfish/LINKS,v 1.4 2005/11/16 13:09:47 solar Exp $

vendor/bcrypt/Makefile

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#
2+
# Written and revised by Solar Designer <solar at openwall.com> in 2000-2011.
3+
# No copyright is claimed, and the software is hereby placed in the public
4+
# domain. In case this attempt to disclaim copyright and place the software
5+
# in the public domain is deemed null and void, then the software is
6+
# Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
7+
# general public under the following terms:
8+
#
9+
# Redistribution and use in source and binary forms, with or without
10+
# modification, are permitted.
11+
#
12+
# There's ABSOLUTELY NO WARRANTY, express or implied.
13+
#
14+
# See crypt_blowfish.c for more information.
15+
#
16+
17+
CC = gcc
18+
AS = $(CC)
19+
LD = $(CC)
20+
RM = rm -f
21+
CFLAGS = -W -Wall -Wbad-function-cast -Wcast-align -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Wundef -Wpointer-arith -O2 -fomit-frame-pointer -funroll-loops
22+
ASFLAGS = -c
23+
LDFLAGS = -s
24+
25+
BLOWFISH_OBJS = \
26+
crypt_blowfish.o x86.o
27+
28+
CRYPT_OBJS = \
29+
$(BLOWFISH_OBJS) crypt_gensalt.o wrapper.o
30+
31+
TEST_OBJS = \
32+
$(BLOWFISH_OBJS) crypt_gensalt.o crypt_test.o
33+
34+
TEST_THREADS_OBJS = \
35+
$(BLOWFISH_OBJS) crypt_gensalt.o crypt_test_threads.o
36+
37+
EXTRA_MANS = \
38+
crypt_r.3 crypt_rn.3 crypt_ra.3 \
39+
crypt_gensalt.3 crypt_gensalt_rn.3 crypt_gensalt_ra.3
40+
41+
all: $(CRYPT_OBJS) man
42+
43+
check: crypt_test
44+
./crypt_test
45+
46+
crypt_test: $(TEST_OBJS)
47+
$(LD) $(LDFLAGS) $(TEST_OBJS) -o $@
48+
49+
crypt_test.o: wrapper.c ow-crypt.h crypt_blowfish.h crypt_gensalt.h
50+
$(CC) -c $(CFLAGS) wrapper.c -DTEST -o $@
51+
52+
check_threads: crypt_test_threads
53+
./crypt_test_threads
54+
55+
crypt_test_threads: $(TEST_THREADS_OBJS)
56+
$(LD) $(LDFLAGS) $(TEST_THREADS_OBJS) -lpthread -o $@
57+
58+
crypt_test_threads.o: wrapper.c ow-crypt.h crypt_blowfish.h crypt_gensalt.h
59+
$(CC) -c $(CFLAGS) wrapper.c -DTEST -DTEST_THREADS=4 -o $@
60+
61+
man: $(EXTRA_MANS)
62+
63+
$(EXTRA_MANS):
64+
echo '.so man3/crypt.3' > $@
65+
66+
crypt_blowfish.o: crypt_blowfish.h
67+
crypt_gensalt.o: crypt_gensalt.h
68+
wrapper.o: crypt.h ow-crypt.h crypt_blowfish.h crypt_gensalt.h
69+
70+
.c.o:
71+
$(CC) -c $(CFLAGS) $*.c
72+
73+
.S.o:
74+
$(AS) $(ASFLAGS) $*.S
75+
76+
clean:
77+
$(RM) crypt_test crypt_test_threads *.o $(EXTRA_MANS) core

vendor/bcrypt/PERFORMANCE

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
These numbers are for 32 iterations ("$2a$05"):
2+
3+
OpenBSD 3.0 bcrypt(*) crypt_blowfish 0.4.4
4+
Pentium III, 840 MHz 99 c/s 121 c/s (+22%)
5+
Alpha 21164PC, 533 MHz 55.5 c/s 76.9 c/s (+38%)
6+
UltraSparc IIi, 400 MHz 49.9 c/s 52.5 c/s (+5%)
7+
Pentium, 120 MHz 8.8 c/s 20.1 c/s (+128%)
8+
PA-RISC 7100LC, 80 MHz 8.5 c/s 16.3 c/s (+92%)
9+
10+
(*) built with -fomit-frame-pointer -funroll-loops, which I don't
11+
think happens for libcrypt.
12+
13+
Starting with version 1.1 released in June 2011, default builds of
14+
crypt_blowfish invoke a quick self-test on every hash computation.
15+
This has roughly a 4.8% performance impact at "$2a$05", but only a 0.6%
16+
impact at a more typical setting of "$2a$08".
17+
18+
The large speedup for the original Pentium is due to the assembly
19+
code and the weird optimizations this processor requires.
20+
21+
The numbers for password cracking are 2 to 10% higher than those for
22+
crypt_blowfish as certain things may be done out of the loop and the
23+
code doesn't need to be reentrant.
24+
25+
Recent versions of John the Ripper (1.6.25-dev and newer) achieve an
26+
additional 15% speedup on the Pentium Pro family of processors (which
27+
includes Pentium III) with a separate version of the assembly code and
28+
run-time CPU detection.
29+
30+
$Owl: Owl/packages/glibc/crypt_blowfish/PERFORMANCE,v 1.6 2011/06/21 12:09:20 solar Exp $

0 commit comments

Comments
 (0)