This commit is contained in:
2025-11-24 14:19:51 +05:30
commit f5c1412b28
6734 changed files with 1527575 additions and 0 deletions

View File

@ -0,0 +1,193 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_PASSWORDS_HPP
#define CAT_PASSWORDS_HPP
#include <cat/rand/IRandom.hpp>
namespace cat {
/*
Why are passwords needed?
Passwords allow the client identity to be proved to the server. Usually
the server identity is already proven by a secure connection, but the
client identity is unproven until a password is provided.
This could be done using signatures with two-way authentication in the
secure connection establishment, however this is very slow and roughly
doubles the work a server has to do to accept a connection.
Passwords seem simple to implement at first but then you start imagining all
the "what ifs":
What if the password database is stolen?
The user passwords had better not be stored in plaintext!
So they must be hashed.
What if two users have the same password?
They would look the same hashed, so hash in the name with the password.
What if name:password for two users is "me : pass" and "m : epass" ?
They would look the same hashed, so insert a separator between the two
before hashing, as in "me\r\npass" instead of just hashing "mepass".
What if someone tries to crack the passwords?
The password hash should be strengthened to make it harder to crack,
by applying the hash function repeatedly. This does reduce the number of
bits of entropy due to non-ideal hash function collisions, but this small
effect is offset by the added difficulty to crack the password.
What if the user has a common name like "dave"?
The password hash should be salted to avoid falling to rainbow tables.
What if the attacker tries to use the stolen password hash to log into the server?
The client provides a hash pre-image during login, so the attacker would need
to reverse the hash to do this, which is harder than cracking the password.
What if an attacker has a way to actively or passively listen to login attempts?
Then they would be able to steal the hash pre-image which would offer some
amount of text password protection but would allow the attacker to log in
as the user without knowing the password.
Rainbow tables can be used to crack the pre-image to determine the password.
If the connection is secure then the client has verified the server identity
and these attacks are only possible through software bugs in the server.
Protocol Description:
H: 256-bit hash function Skein-256
c: Client
s: Server
X: Strengthening factor
Protocol 1: Creating a User Account
c: Choose a name N
c: Choose a password P
c: Calculate I = H("N\r\nP") strengthened X times
c2s CREATE_ACCOUNT || N || I (32 by)
s: Verify account is not taken, then creates an account in the database
s: Generate a 32-bit random salt S
s: Calculate J = H(S || I)
s: Store N, J and S in the account
s2c ACCOUNT_CREATE_SUCCESS
Protocol 2: Logging into a User Account
c: Choose a name N
c: Choose a password P
c: Calculate I = H("N\r\nP") strengthened X times
c2s LOGIN_REQUEST || N || I (32 by)
s: Verify user name exists in account database
s: Retrieve J, S from the account database
s: Calculate J' = H(S || I)
s: Verify J = J'
s2c LOGIN_SUCCESS
What if the user creates an account through a website?
The server-side script on the website should execute a program that runs
Protocol 1 to create the user account.
Why use this sort of password authentication + Tunnel instead of SRP?
Performance. The Tunnel key agreement takes much less CPU time than SRP
and authenticates the server, which allows this protocol to be run without
any disadvantages.
*/
class PasswordBase
{
public:
static const int HASH_BITS = 256;
static const int HASH_BYTES = HASH_BITS / 8;
static const int STRENGTHENING_FACTOR = 1000;
};
class PasswordCreator : public PasswordBase
{
public:
// Hash a password for transmission to the server during account creation or login
// NOTE: You should make the name all lowercase before passing it in
// Returns false on failure
bool HashPassword(const void *in_name, int name_bytes,
const void *in_password, int password_bytes,
void *out_hash /* 32 bytes */);
// This version accepts non-unicode C strings with nul-terminators
// It converts the name to lowercase internally
bool HashPasswordString(const char *in_name,
const char *in_password,
void *out_hash /* 32 bytes */);
};
class PasswordVerifier : public PasswordBase
{
public:
// Salt a hash for storage in the account database during account creation
// Generates a 32-bit salt and a 256-bit hash for storage
// Returns false on failure
bool SaltHash(IRandom *prng,
const void *in_hash /* 32 bytes */,
void *out_salted_hash /* 32 bytes */,
u32 *out_salt /* 4 bytes */);
// Given the stored salted hash and salt, verify the client's password hash during login
// Returns false if password is incorrect
bool VerifyHash(const void *in_hash /* 32 bytes */,
const void *in_salted_hash /* 32 bytes */,
u32 in_salt /* 4 bytes */);
};
} // namespace cat
#endif // CAT_PASSWORDS_HPP