194 lines
6.6 KiB
C++
194 lines
6.6 KiB
C++
/*
|
|
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
|