package org.shujito.ucs.models;
import java.security.SecureRandom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import javax.ws.rs.core.Response.Status;
import org.shujito.ucs.ApiException;
import org.shujito.ucs.Constants;
import org.shujito.ucs.Crypto;
import org.shujito.ucs.db.Database;
public class UserPassword
{
public static final String TAG = UserPassword.class.getSimpleName();
public static final String USER_UUID = "user_uuid";
public static final String PASSWORD = "password";
public static final String SALT = "salt";
public static UserPassword fromResultSet(ResultSet rs) throws Exception
{
ResultSetMetaData rsmd = rs.getMetaData();
UserPassword up = new UserPassword();
int count = rsmd.getColumnCount();
for (int idx = 1; idx <= count; idx++)
{
switch (rsmd.getColumnLabel(idx))
{
case USER_UUID:
up.userUuid = rs.getString(idx);
break;
case PASSWORD:
up.password = rs.getBytes(idx);
break;
case SALT:
up.salt = rs.getBytes(idx);
break;
}
}
return up;
}
public static UserPassword fromUsername(String username) throws Exception
{
try (PreparedStatement psm = Database.prepareStatement("select "
+ "users.uuid as user_uuid,"
+ "user_passwords.password as password,"
+ "user_passwords.salt as salt"
+ " from users"
+ " inner join user_passwords"
+ " on users.uuid=user_passwords.user_uuid"
+ " where users.username=lower(?)"))
{
psm.setString(1, username);
try (ResultSet rs = psm.executeQuery())
{
if (!rs.next())
throw new ApiException(Constants.Strings.USER_DOES_NOT_EXIST, Status.NOT_FOUND.getStatusCode());
return UserPassword.fromResultSet(rs);
}
}
}
public String userUuid;
public byte[] password;
public byte[] salt;
private UserPassword()
{
}
public UserPassword(String password)
{
this.password = password.getBytes();
this.hashPassword();
}
public UserPassword(byte[] password, byte[] salt)
{
this.password = password;
this.salt = salt;
this.hashPassword(salt);
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!(obj instanceof UserPassword))
return false;
UserPassword cast = UserPassword.class.cast(obj);
int diff = this.password.length ^ cast.password.length;
for (int idx = 0; idx < this.password.length && idx < cast.password.length; idx++)
diff |= this.password[idx] ^ cast.password[idx];
return diff == 0;
}
public void hashPassword()
{
byte[] saltBytes = new byte[32];
new SecureRandom().nextBytes(saltBytes);
this.hashPassword(saltBytes);
}
public void hashPassword(byte[] saltBytes)
{
byte[] passwordBytes = this.password;
byte[] saltedPasswordBytes = new byte[passwordBytes.length + saltBytes.length];
for (int idx = 0; idx < saltBytes.length; idx++)
saltedPasswordBytes[idx] = saltBytes[idx];
for (int idx = 0; idx < passwordBytes.length; idx++)
saltedPasswordBytes[idx + saltBytes.length] = passwordBytes[idx];
// hash it
byte[] sha256passwordBytes = Crypto.sha256(saltedPasswordBytes);
// stretchy
byte[] bytesContainer = new byte[sha256passwordBytes.length + saltBytes.length];
for (int idx = 0; idx < 0x7ffff; idx++)
{
for (int jdx = 0; jdx < saltBytes.length; jdx++)
bytesContainer[jdx] = saltBytes[jdx];
for (int jdx = 0; jdx < sha256passwordBytes.length; jdx++)
bytesContainer[jdx + saltBytes.length] = sha256passwordBytes[jdx];
sha256passwordBytes = Crypto.sha256(bytesContainer);
}
this.password = sha256passwordBytes;
this.salt = saltBytes;
}
public void save(String username) throws Exception
{
try (PreparedStatement psm = Database
.prepareStatement("insert into user_passwords(user_uuid,password,salt) select users.uuid as user_uuid,? as password,? as salt from users where users.username = lower(?)"))
{
psm.setBytes(1, this.password);
psm.setBytes(2, this.salt);
psm.setString(3, username);
psm.executeUpdate();
}
}
}